[gnome-builder: 25/139] buildsystem: move components to libide-foundry and libide-threading



commit f7ac6141aac8656fbbbbf74bc95d1ffb15fc60ad
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jan 9 15:23:53 2019 -0800

    buildsystem: move components to libide-foundry and libide-threading
    
    The buildsystem components are moved by this commit into the upcoming
    libide-foundry static library, where verious code foundry tooling is to be
    located.
    
    Environments are more generally useful, and they are moved into the
    upcoming libide-threading static library.

 src/libide/buildsystem/OVERVIEW.md                 |  54 ---
 src/libide/buildsystem/ide-build-target.h          |  64 ----
 src/libide/buildsystem/ide-build-utils.h           |  31 --
 src/libide/buildsystem/meson.build                 |  58 ---
 .../ide-build-log-private.h                        |   2 +-
 .../{buildsystem => foundry}/ide-build-log.c       |   6 +-
 .../{buildsystem => foundry}/ide-build-log.h       |   4 +
 .../{buildsystem => foundry}/ide-build-manager.c   | 173 ++++++---
 .../{buildsystem => foundry}/ide-build-manager.h   |  24 +-
 .../ide-build-pipeline-addin.c                     |  11 +-
 .../ide-build-pipeline-addin.h                     |   8 +-
 .../{buildsystem => foundry}/ide-build-pipeline.c  | 417 ++++++++++++++-------
 .../{buildsystem => foundry}/ide-build-pipeline.h  |  53 ++-
 .../{buildsystem => foundry}/ide-build-private.h   |   7 +-
 .../ide-build-stage-launcher.c                     |  11 +-
 .../ide-build-stage-launcher.h                     |  17 +-
 .../ide-build-stage-mkdirs.c                       |   8 +-
 .../ide-build-stage-mkdirs.h                       |  13 +-
 .../ide-build-stage-private.h                      |   4 +-
 .../ide-build-stage-transfer.c                     |  44 ++-
 .../ide-build-stage-transfer.h                     |   8 +-
 .../{buildsystem => foundry}/ide-build-stage.c     |  73 +++-
 .../{buildsystem => foundry}/ide-build-stage.h     |  82 +---
 .../ide-build-system-discovery.c                   |   2 +-
 .../ide-build-system-discovery.h                   |   8 +-
 .../{buildsystem => foundry}/ide-build-system.c    | 172 ++-------
 .../{buildsystem => foundry}/ide-build-system.h    |  25 +-
 .../ide-build-target-provider.c                    |  10 +-
 .../ide-build-target-provider.h                    |   7 +-
 .../{buildsystem => foundry}/ide-build-target.c    |  46 ++-
 src/libide/foundry/ide-build-target.h              |  80 ++++
 .../{buildsystem => foundry}/ide-build-utils.c     |  12 +-
 .../ide-compile-commands.c                         |  12 +-
 .../ide-compile-commands.h                         |   6 +-
 .../ide-dependency-updater.c                       |   0
 .../ide-dependency-updater.h                       |   7 +-
 .../foundry/ide-simple-build-system-discovery.c    | 374 ++++++++++++++++++
 .../foundry/ide-simple-build-system-discovery.h    |  62 +++
 .../ide-simple-build-target.c                      |   5 +-
 .../ide-simple-build-target.h                      |   9 +-
 .../ide-environment-variable.c                     |   2 +-
 .../ide-environment-variable.h                     |   6 +-
 .../{buildsystem => threading}/ide-environment.c   |  47 ++-
 .../{buildsystem => threading}/ide-environment.h   |  13 +-
 44 files changed, 1347 insertions(+), 730 deletions(-)
---
diff --git a/src/libide/buildsystem/ide-build-log-private.h b/src/libide/foundry/ide-build-log-private.h
similarity index 97%
rename from src/libide/buildsystem/ide-build-log-private.h
rename to src/libide/foundry/ide-build-log-private.h
index 9a91bd2ad..c0ecca3ed 100644
--- a/src/libide/buildsystem/ide-build-log-private.h
+++ b/src/libide/foundry/ide-build-log-private.h
@@ -22,7 +22,7 @@
 
 #include <gio/gio.h>
 
-#include "buildsystem/ide-build-log.h"
+#include "ide-build-log.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/buildsystem/ide-build-log.c b/src/libide/foundry/ide-build-log.c
similarity index 98%
rename from src/libide/buildsystem/ide-build-log.c
rename to src/libide/foundry/ide-build-log.c
index c9488267c..ba0cdb180 100644
--- a/src/libide/buildsystem/ide-build-log.c
+++ b/src/libide/foundry/ide-build-log.c
@@ -22,11 +22,11 @@
 
 #include "config.h"
 
+#include <libide-core.h>
 #include <string.h>
 
-#include "application/ide-application.h"
-#include "buildsystem/ide-build-log.h"
-#include "buildsystem/ide-build-log-private.h"
+#include "ide-build-log.h"
+#include "ide-build-log-private.h"
 
 #define POINTER_MARK(p)   GSIZE_TO_POINTER(GPOINTER_TO_SIZE(p)|1)
 #define POINTER_UNMARK(p) GSIZE_TO_POINTER(GPOINTER_TO_SIZE(p)&~(gsize)1)
diff --git a/src/libide/buildsystem/ide-build-log.h b/src/libide/foundry/ide-build-log.h
similarity index 89%
rename from src/libide/buildsystem/ide-build-log.h
rename to src/libide/foundry/ide-build-log.h
index d0a5bd146..e7f42b32f 100644
--- a/src/libide/buildsystem/ide-build-log.h
+++ b/src/libide/foundry/ide-build-log.h
@@ -20,6 +20,10 @@
 
 #pragma once
 
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
 #include <glib.h>
 
 G_BEGIN_DECLS
diff --git a/src/libide/buildsystem/ide-build-manager.c b/src/libide/foundry/ide-build-manager.c
similarity index 92%
rename from src/libide/buildsystem/ide-build-manager.c
rename to src/libide/foundry/ide-build-manager.c
index 66c68140e..9ab244b6b 100644
--- a/src/libide/buildsystem/ide-build-manager.c
+++ b/src/libide/foundry/ide-build-manager.c
@@ -24,28 +24,24 @@
 
 #include <dazzle.h>
 #include <glib/gi18n.h>
-
-#include "ide-context.h"
-#include "ide-debug.h"
-
-#include "buffers/ide-buffer.h"
-#include "buffers/ide-buffer-manager.h"
-#include "buildsystem/ide-build-manager.h"
-#include "buildsystem/ide-build-pipeline.h"
-#include "buildsystem/ide-build-private.h"
-#include "config/ide-configuration-manager.h"
-#include "config/ide-configuration.h"
-#include "devices/ide-device.h"
-#include "devices/ide-device-manager.h"
-#include "diagnostics/ide-diagnostic.h"
-#include "diagnostics/ide-diagnostics-manager.h"
-#include "runtimes/ide-runtime.h"
-#include "runtimes/ide-runtime-manager.h"
-#include "runtimes/ide-runtime-private.h"
-#include "threading/ide-task.h"
-#include "toolchain/ide-toolchain-manager.h"
-#include "toolchain/ide-toolchain-private.h"
-#include "vcs/ide-vcs.h"
+#include <libide-code.h>
+#include <libide-threading.h>
+#include <libide-vcs.h>
+
+#include "ide-build-manager.h"
+#include "ide-build-pipeline.h"
+#include "ide-build-private.h"
+#include "ide-configuration-manager.h"
+#include "ide-configuration.h"
+#include "ide-device-info.h"
+#include "ide-device-manager.h"
+#include "ide-device.h"
+#include "ide-foundry-compat.h"
+#include "ide-runtime-manager.h"
+#include "ide-runtime-private.h"
+#include "ide-runtime.h"
+#include "ide-toolchain-manager.h"
+#include "ide-toolchain-private.h"
 
 /**
  * SECTION:ide-build-manager
@@ -444,7 +440,7 @@ ide_build_manager_ensure_runtime_cb (GObject      *object,
   context = ide_object_get_context (IDE_OBJECT (pipeline));
   g_assert (IDE_IS_CONTEXT (context));
 
-  toolchain_manager = ide_context_get_toolchain_manager (context);
+  toolchain_manager = ide_toolchain_manager_from_context (context);
   g_assert (IDE_IS_TOOLCHAIN_MANAGER (toolchain_manager));
 
   _ide_toolchain_manager_prepare_async (toolchain_manager,
@@ -493,7 +489,7 @@ ide_build_manager_device_get_info_cb (GObject      *object,
   context = ide_object_get_context (IDE_OBJECT (pipeline));
   g_assert (IDE_IS_CONTEXT (context));
 
-  runtime_manager = ide_context_get_runtime_manager (context);
+  runtime_manager = ide_runtime_manager_from_context (context);
   g_assert (IDE_IS_RUNTIME_MANAGER (runtime_manager));
 
   if (!(info = ide_device_get_info_finish (device, result, &error)))
@@ -553,7 +549,7 @@ ide_build_manager_invalidate_pipeline (IdeBuildManager *self)
    */
   ide_build_manager_cancel (self);
 
-  g_clear_object (&self->pipeline);
+  ide_clear_and_destroy_object (&self->pipeline);
 
   g_clear_pointer (&self->running_time, g_timer_destroy);
 
@@ -562,11 +558,11 @@ ide_build_manager_invalidate_pipeline (IdeBuildManager *self)
   self->warning_count = 0;
 
   /* Don't setup anything new if we're in shutdown */
-  if (ide_context_is_unloading (context))
+  if (ide_object_in_destruction (IDE_OBJECT (context)))
     IDE_EXIT;
 
-  config_manager = ide_context_get_configuration_manager (context);
-  device_manager = ide_context_get_device_manager (context);
+  config_manager = ide_configuration_manager_from_context (context);
+  device_manager = ide_device_manager_from_context (context);
 
   config = ide_configuration_manager_get_current (config_manager);
   device = ide_device_manager_get_device (device_manager);
@@ -581,10 +577,10 @@ ide_build_manager_invalidate_pipeline (IdeBuildManager *self)
    */
   ide_build_manager_set_can_build (self, FALSE);
   self->pipeline = g_object_new (IDE_TYPE_BUILD_PIPELINE,
-                                 "context", context,
                                  "configuration", config,
                                  "device", device,
                                  NULL);
+  ide_object_append (IDE_OBJECT (self), IDE_OBJECT (self->pipeline));
   dzl_signal_group_set_target (self->pipeline_signals, self->pipeline);
 
   /*
@@ -631,7 +627,7 @@ ide_build_manager_vcs_changed (IdeBuildManager *self,
 
   branch_name = ide_vcs_get_branch_name (vcs);
 
-  if (!dzl_str_equal0 (branch_name, self->branch_name))
+  if (!ide_str_equal0 (branch_name, self->branch_name))
     {
       g_free (self->branch_name);
       self->branch_name = g_strdup (branch_name);
@@ -656,9 +652,9 @@ initable_init (GInitable     *initable,
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  config_manager = ide_context_get_configuration_manager (context);
-  device_manager = ide_context_get_device_manager (context);
-  vcs = ide_context_get_vcs (context);
+  config_manager = ide_configuration_manager_from_context (context);
+  device_manager = ide_device_manager_from_context (context);
+  vcs = ide_vcs_from_context (context);
 
   self->branch_name = ide_vcs_get_branch_name (vcs);
 
@@ -751,8 +747,8 @@ ide_build_manager_real_build_finished (IdeBuildManager  *self,
    */
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  diagnostics = ide_context_get_diagnostics_manager (context);
-  bufmgr = ide_context_get_buffer_manager (context);
+  diagnostics = ide_diagnostics_manager_from_context (context);
+  bufmgr = ide_buffer_manager_from_context (context);
   n_items = g_list_model_get_n_items (G_LIST_MODEL (bufmgr));
 
   for (guint i = 0; i < n_items; i++)
@@ -776,7 +772,7 @@ ide_build_manager_finalize (GObject *object)
 {
   IdeBuildManager *self = (IdeBuildManager *)object;
 
-  g_clear_object (&self->pipeline);
+  ide_clear_and_destroy_object (&self->pipeline);
   g_clear_object (&self->pipeline_signals);
   g_clear_object (&self->cancellable);
   g_clear_pointer (&self->last_build_time, g_date_time_unref);
@@ -1080,7 +1076,7 @@ ide_build_manager_action_build (IdeBuildManager *self,
 
   g_assert (IDE_IS_BUILD_MANAGER (self));
 
-  ide_build_manager_execute_async (self, IDE_BUILD_PHASE_BUILD, NULL, NULL, NULL);
+  ide_build_manager_execute_async (self, IDE_BUILD_PHASE_BUILD, NULL, NULL, NULL, NULL);
 
   IDE_EXIT;
 }
@@ -1093,7 +1089,7 @@ ide_build_manager_action_rebuild (IdeBuildManager *self,
 
   g_assert (IDE_IS_BUILD_MANAGER (self));
 
-  ide_build_manager_rebuild_async (self, IDE_BUILD_PHASE_BUILD, NULL, NULL, NULL);
+  ide_build_manager_rebuild_async (self, IDE_BUILD_PHASE_BUILD, NULL, NULL, NULL, NULL);
 
   IDE_EXIT;
 }
@@ -1119,7 +1115,7 @@ ide_build_manager_action_install (IdeBuildManager *self,
 
   g_assert (IDE_IS_BUILD_MANAGER (self));
 
-  ide_build_manager_execute_async (self, IDE_BUILD_PHASE_INSTALL, NULL, NULL, NULL);
+  ide_build_manager_execute_async (self, IDE_BUILD_PHASE_INSTALL, NULL, NULL, NULL, NULL);
 
   IDE_EXIT;
 }
@@ -1132,7 +1128,7 @@ ide_build_manager_action_export (IdeBuildManager *self,
 
   g_assert (IDE_IS_BUILD_MANAGER (self));
 
-  ide_build_manager_execute_async (self, IDE_BUILD_PHASE_EXPORT, NULL, NULL, NULL);
+  ide_build_manager_execute_async (self, IDE_BUILD_PHASE_EXPORT, NULL, NULL, NULL, NULL);
 
   IDE_EXIT;
 }
@@ -1317,11 +1313,36 @@ ide_build_manager_cancel (IdeBuildManager *self)
 IdeBuildPipeline *
 ide_build_manager_get_pipeline (IdeBuildManager *self)
 {
+  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), NULL);
   g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), NULL);
 
   return self->pipeline;
 }
 
+/**
+ * ide_build_manager_ref_pipeline:
+ * @self: a #IdeBuildManager
+ *
+ * A thread-safe variant of ide_build_manager_get_pipeline().
+ *
+ * Returns: (transfer full) (nullable): an #IdeBuildPipeline or %NULL
+ *
+ * Since: 3.32
+ */
+IdeBuildPipeline *
+ide_build_manager_ref_pipeline (IdeBuildManager *self)
+{
+  IdeBuildPipeline *ret = NULL;
+
+  g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), NULL);
+
+  ide_object_lock (IDE_OBJECT (self));
+  g_set_object (&ret, self->pipeline);
+  ide_object_unlock (IDE_OBJECT (self));
+
+  return g_steal_pointer (&ret);
+}
+
 static void
 ide_build_manager_execute_cb (GObject      *object,
                               GAsyncResult *result,
@@ -1360,6 +1381,8 @@ ide_build_manager_save_all_cb (GObject      *object,
   g_autoptr(GError) error = NULL;
   IdeBuildManager *self;
   GCancellable *cancellable;
+  GPtrArray *targets;
+  IdeBuildPhase phase;
 
   IDE_ENTRY;
 
@@ -1368,6 +1391,7 @@ ide_build_manager_save_all_cb (GObject      *object,
 
   self = ide_task_get_source_object (task);
   cancellable = ide_task_get_cancellable (task);
+  targets = ide_task_get_task_data (task);
 
   g_assert (IDE_IS_BUILD_MANAGER (self));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
@@ -1378,10 +1402,14 @@ ide_build_manager_save_all_cb (GObject      *object,
       IDE_EXIT;
     }
 
-  ide_build_pipeline_execute_async (self->pipeline,
-                                    cancellable,
-                                    ide_build_manager_execute_cb,
-                                    g_steal_pointer (&task));
+  phase = ide_build_pipeline_get_requested_phase (self->pipeline);
+
+  ide_build_pipeline_build_targets_async (self->pipeline,
+                                          phase,
+                                          targets,
+                                          cancellable,
+                                          ide_build_manager_execute_cb,
+                                          g_steal_pointer (&task));
 
   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_DIAGNOSTICS]);
   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_LAST_BUILD_TIME]);
@@ -1394,6 +1422,8 @@ ide_build_manager_save_all_cb (GObject      *object,
  * ide_build_manager_execute_async:
  * @self: An #IdeBuildManager
  * @phase: An #IdeBuildPhase or 0
+ * @targets: (nullable) (element-type IdeBuildTarget): an array of
+ *   #IdeBuildTarget to build
  * @cancellable: (nullable): a #GCancellable or %NULL
  * @callback: A callback to execute upon completion
  * @user_data: user data for @callback
@@ -1408,6 +1438,7 @@ ide_build_manager_save_all_cb (GObject      *object,
 void
 ide_build_manager_execute_async (IdeBuildManager     *self,
                                  IdeBuildPhase        phase,
+                                 GPtrArray           *targets,
                                  GCancellable        *cancellable,
                                  GAsyncReadyCallback  callback,
                                  gpointer             user_data)
@@ -1429,6 +1460,9 @@ ide_build_manager_execute_async (IdeBuildManager     *self,
   ide_task_set_priority (task, G_PRIORITY_LOW);
   ide_task_set_return_on_cancel (task, TRUE);
 
+  if (targets != NULL)
+    ide_task_set_task_data (task, _g_ptr_array_copy_objects (targets), g_ptr_array_unref);
+
   if (self->pipeline == NULL ||
       self->can_build == FALSE ||
       !ide_build_pipeline_is_ready (self->pipeline))
@@ -1468,7 +1502,7 @@ ide_build_manager_execute_async (IdeBuildManager     *self,
   if ((phase & IDE_BUILD_PHASE_MASK) >= IDE_BUILD_PHASE_BUILD)
     {
       context = ide_object_get_context (IDE_OBJECT (self));
-      buffer_manager = ide_context_get_buffer_manager (context);
+      buffer_manager = ide_buffer_manager_from_context (context);
       ide_buffer_manager_save_all_async (buffer_manager,
                                          cancellable,
                                          ide_build_manager_save_all_cb,
@@ -1476,11 +1510,12 @@ ide_build_manager_execute_async (IdeBuildManager     *self,
       IDE_EXIT;
     }
 
-  ide_build_pipeline_build_async (self->pipeline,
-                                  phase,
-                                  cancellable,
-                                  ide_build_manager_execute_cb,
-                                  g_steal_pointer (&task));
+  ide_build_pipeline_build_targets_async (self->pipeline,
+                                          phase,
+                                          targets,
+                                          cancellable,
+                                          ide_build_manager_execute_cb,
+                                          g_steal_pointer (&task));
 
   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ERROR_COUNT]);
   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HAS_DIAGNOSTICS]);
@@ -1659,6 +1694,8 @@ ide_build_manager_rebuild_cb (GObject      *object,
  * ide_build_manager_rebuild_async:
  * @self: a #IdeBuildManager
  * @phase: the build phase to rebuild to
+ * @targets: (element-type IdeBuildTarget) (nullable): an array of #GPtrArray
+ *   of #IdeBuildTarget or %NULL.
  * @cancellable: (nullable): a #GCancellable or %NULL
  * @callback: (nullable): a callback to execute upon completion, or %NULL
  * @user_data: closure data for @callback
@@ -1674,6 +1711,7 @@ ide_build_manager_rebuild_cb (GObject      *object,
 void
 ide_build_manager_rebuild_async (IdeBuildManager     *self,
                                  IdeBuildPhase        phase,
+                                 GPtrArray           *targets,
                                  GCancellable        *cancellable,
                                  GAsyncReadyCallback  callback,
                                  gpointer             user_data)
@@ -1704,6 +1742,7 @@ ide_build_manager_rebuild_async (IdeBuildManager     *self,
 
   ide_build_pipeline_rebuild_async (self->pipeline,
                                     phase,
+                                    targets,
                                     cancellable,
                                     ide_build_manager_rebuild_cb,
                                     g_steal_pointer (&task));
@@ -1773,3 +1812,37 @@ ide_build_manager_set_can_build (IdeBuildManager *self,
       ide_build_manager_update_action_enabled (self);
     }
 }
+
+/**
+ * ide_build_manager_invalidate:
+ * @self: a #IdeBuildManager
+ *
+ * Requests that the #IdeBuildManager invalidate the current pipeline and
+ * setup a new pipeline.
+ *
+ * Since: 3.32
+ */
+void
+ide_build_manager_invalidate (IdeBuildManager *self)
+{
+  g_return_if_fail (IDE_IS_MAIN_THREAD ());
+  g_return_if_fail (IDE_IS_BUILD_MANAGER (self));
+
+  ide_build_manager_invalidate_pipeline (self);
+}
+
+guint
+ide_build_manager_get_error_count (IdeBuildManager *self)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), 0);
+
+  return self->error_count;
+}
+
+guint
+ide_build_manager_get_warning_count (IdeBuildManager *self)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_MANAGER (self), 0);
+
+  return self->warning_count;
+}
diff --git a/src/libide/buildsystem/ide-build-manager.h b/src/libide/foundry/ide-build-manager.h
similarity index 79%
rename from src/libide/buildsystem/ide-build-manager.h
rename to src/libide/foundry/ide-build-manager.h
index 2f0dcf7ca..3a761d47d 100644
--- a/src/libide/buildsystem/ide-build-manager.h
+++ b/src/libide/foundry/ide-build-manager.h
@@ -20,13 +20,13 @@
 
 #pragma once
 
-#include <gio/gio.h>
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <libide-core.h>
 
-#include "ide-object.h"
-
-#include "buildsystem/ide-build-pipeline.h"
+#include "ide-build-pipeline.h"
 
 G_BEGIN_DECLS
 
@@ -35,23 +35,36 @@ G_BEGIN_DECLS
 IDE_AVAILABLE_IN_3_32
 G_DECLARE_FINAL_TYPE (IdeBuildManager, ide_build_manager, IDE, BUILD_MANAGER, IdeObject)
 
+IDE_AVAILABLE_IN_3_32
+IdeBuildManager  *ide_build_manager_from_context        (IdeContext *context);
+IDE_AVAILABLE_IN_3_32
+IdeBuildManager  *ide_build_manager_ref_from_context    (IdeContext *context);
 IDE_AVAILABLE_IN_3_32
 gboolean          ide_build_manager_get_busy            (IdeBuildManager       *self);
 IDE_AVAILABLE_IN_3_32
 gboolean          ide_build_manager_get_can_build       (IdeBuildManager       *self);
 IDE_AVAILABLE_IN_3_32
+guint             ide_build_manager_get_error_count     (IdeBuildManager       *self);
+IDE_AVAILABLE_IN_3_32
+guint             ide_build_manager_get_warning_count   (IdeBuildManager       *self);
+IDE_AVAILABLE_IN_3_32
 gchar            *ide_build_manager_get_message         (IdeBuildManager       *self);
 IDE_AVAILABLE_IN_3_32
 GDateTime        *ide_build_manager_get_last_build_time (IdeBuildManager       *self);
 IDE_AVAILABLE_IN_3_32
 GTimeSpan         ide_build_manager_get_running_time    (IdeBuildManager       *self);
 IDE_AVAILABLE_IN_3_32
+void              ide_build_manager_invalidate          (IdeBuildManager       *self);
+IDE_AVAILABLE_IN_3_32
 void              ide_build_manager_cancel              (IdeBuildManager       *self);
 IDE_AVAILABLE_IN_3_32
 IdeBuildPipeline *ide_build_manager_get_pipeline        (IdeBuildManager       *self);
 IDE_AVAILABLE_IN_3_32
+IdeBuildPipeline *ide_build_manager_ref_pipeline        (IdeBuildManager       *self);
+IDE_AVAILABLE_IN_3_32
 void              ide_build_manager_rebuild_async       (IdeBuildManager       *self,
                                                          IdeBuildPhase          phase,
+                                                         GPtrArray             *targets,
                                                          GCancellable          *cancellable,
                                                          GAsyncReadyCallback    callback,
                                                          gpointer               user_data);
@@ -62,6 +75,7 @@ gboolean          ide_build_manager_rebuild_finish      (IdeBuildManager       *
 IDE_AVAILABLE_IN_3_32
 void              ide_build_manager_execute_async       (IdeBuildManager       *self,
                                                          IdeBuildPhase          phase,
+                                                         GPtrArray             *targets,
                                                          GCancellable          *cancellable,
                                                          GAsyncReadyCallback    callback,
                                                          gpointer               user_data);
diff --git a/src/libide/buildsystem/ide-build-pipeline-addin.c b/src/libide/foundry/ide-build-pipeline-addin.c
similarity index 91%
rename from src/libide/buildsystem/ide-build-pipeline-addin.c
rename to src/libide/foundry/ide-build-pipeline-addin.c
index 5d05bddf0..7d3f5db1e 100644
--- a/src/libide/buildsystem/ide-build-pipeline-addin.c
+++ b/src/libide/foundry/ide-build-pipeline-addin.c
@@ -22,9 +22,8 @@
 
 #include "config.h"
 
-#include "ide-context.h"
-
-#include "buildsystem/ide-build-pipeline-addin.h"
+#include "ide-build-pipeline.h"
+#include "ide-build-pipeline-addin.h"
 
 G_DEFINE_INTERFACE (IdeBuildPipelineAddin, ide_build_pipeline_addin, IDE_TYPE_OBJECT)
 
@@ -65,7 +64,7 @@ ide_build_pipeline_addin_unload (IdeBuildPipelineAddin *self,
         {
           guint stage_id = g_array_index (ar, guint, i);
 
-          ide_build_pipeline_disconnect (pipeline, stage_id);
+          ide_build_pipeline_detach (pipeline, stage_id);
         }
     }
 }
@@ -73,10 +72,10 @@ ide_build_pipeline_addin_unload (IdeBuildPipelineAddin *self,
 /**
  * ide_build_pipeline_addin_track:
  * @self: An #IdeBuildPipelineAddin
- * @stage_id: a stage id returned from ide_build_pipeline_connect()
+ * @stage_id: a stage id returned from ide_build_pipeline_attach()
  *
  * This function will track the stage_id that was returned from
- * ide_build_pipeline_connect() or similar functions. Doing so results in
+ * ide_build_pipeline_attach() or similar functions. Doing so results in
  * the stage being automatically disconnected when the addin is unloaded.
  *
  * This means that many #IdeBuildPipelineAddin implementations do not need
diff --git a/src/libide/buildsystem/ide-build-pipeline-addin.h b/src/libide/foundry/ide-build-pipeline-addin.h
similarity index 90%
rename from src/libide/buildsystem/ide-build-pipeline-addin.h
rename to src/libide/foundry/ide-build-pipeline-addin.h
index 4648a523f..6341e8cf9 100644
--- a/src/libide/buildsystem/ide-build-pipeline-addin.h
+++ b/src/libide/foundry/ide-build-pipeline-addin.h
@@ -20,11 +20,13 @@
 
 #pragma once
 
-#include <gio/gio.h>
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <libide-core.h>
 
-#include "buildsystem/ide-build-pipeline.h"
+#include "ide-build-pipeline.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/buildsystem/ide-build-pipeline.c b/src/libide/foundry/ide-build-pipeline.c
similarity index 90%
rename from src/libide/buildsystem/ide-build-pipeline.c
rename to src/libide/foundry/ide-build-pipeline.c
index 874e7f52b..e3c8ff91b 100644
--- a/src/libide/buildsystem/ide-build-pipeline.c
+++ b/src/libide/foundry/ide-build-pipeline.c
@@ -24,43 +24,37 @@
 
 #include <glib/gi18n.h>
 #include <dazzle.h>
+#include <libide-plugins.h>
 #include <libpeas/peas.h>
 #include <string.h>
 #include <vte/vte.h>
 
-#include "ide-context.h"
-#include "ide-debug.h"
-#include "ide-enums.h"
-
-#include "application/ide-application.h"
-#include "buildsystem/ide-build-log.h"
-#include "buildsystem/ide-build-log-private.h"
-#include "buildsystem/ide-build-pipeline.h"
-#include "buildsystem/ide-build-pipeline-addin.h"
-#include "buildsystem/ide-build-private.h"
-#include "buildsystem/ide-build-stage.h"
-#include "buildsystem/ide-build-stage-launcher.h"
-#include "buildsystem/ide-build-stage-private.h"
-#include "buildsystem/ide-build-system.h"
-#include "buildsystem/ide-build-utils.h"
-#include "devices/ide-device.h"
-#include "diagnostics/ide-diagnostic.h"
-#include "diagnostics/ide-source-location.h"
-#include "diagnostics/ide-source-range.h"
-#include "files/ide-file.h"
-#include "plugins/ide-extension-util.h"
-#include "projects/ide-project.h"
-#include "runtimes/ide-runtime.h"
-#include "terminal/ide-terminal-util.h"
-#include "toolchain/ide-toolchain-manager.h"
-#include "toolchain/ide-toolchain.h"
-#include "util/ide-line-reader.h"
-#include "util/ide-posix.h"
-#include "util/ptyintercept.h"
-#include "vcs/ide-vcs.h"
-#include "threading/ide-task.h"
+#include <libide-core.h>
+#include <libide-code.h>
+#include <libide-io.h>
+#include <libide-plugins.h>
+#include <libide-projects.h>
+#include <libide-threading.h>
+
+#include "ide-build-log-private.h"
+#include "ide-build-log.h"
+#include "ide-build-pipeline-addin.h"
+#include "ide-build-pipeline.h"
+#include "ide-build-private.h"
+#include "ide-build-stage-launcher.h"
+#include "ide-build-stage-private.h"
+#include "ide-build-stage.h"
+#include "ide-build-system.h"
+#include "ide-device-info.h"
+#include "ide-device.h"
+#include "ide-foundry-compat.h"
+#include "ide-foundry-enums.h"
+#include "ide-runtime.h"
+#include "ide-toolchain-manager.h"
+#include "ide-toolchain.h"
 
 DZL_DEFINE_COUNTER (Instances, "Pipeline", "N Pipelines", "Number of Pipeline instances")
+G_DEFINE_QUARK (ide_build_error, ide_build_error)
 
 /**
  * SECTION:idebuildpipeline
@@ -140,7 +134,7 @@ struct _IdeBuildPipeline
    * them and they might go about adding stages to the pipeline,
    * add error formats, or just monitor logs.
    */
-  PeasExtensionSet *addins;
+  IdeExtensionSetAdapter *addins;
 
   /*
    * This is the configuration for the build. It is a snapshot of
@@ -218,17 +212,17 @@ struct _IdeBuildPipeline
 
   /*
    * The VtePty is used to connect to a VteTerminal. It's basically
-   * just a wrapper around a PTY master. We then add a pty_intercept_t
+   * just a wrapper around a PTY master. We then add a IdePtyIntercept
    * to proxy PTY data while allowing us to tap into the content being
    * transmitted. We can use that to run regexes against and perform
    * additional error extraction. Finally, pty_slave is the PTY device
    * we created that will get attached to stdin/stdout/stderr in our
    * spawned subprocesses. It is a slave to the PTY master owned by
-   * the pty_intercept_t.
+   * the IdePtyIntercept.
    */
   VtePty          *pty;
-  pty_intercept_t  intercept;
-  pty_fd_t         pty_slave;
+  IdePtyIntercept  intercept;
+  IdePtyFd         pty_slave;
 
   /*
    * If the terminal interpreting our Pty has received a terminal
@@ -271,6 +265,13 @@ struct _IdeBuildPipeline
    */
   guint seqnum;
 
+  /* We use a GSource to load addins in an idle callback so that
+   * we don't block the main loop for too long. When disposing the
+   * pipeline, we need to kill that operation too (since it may
+   * lose access to IdeContext in the process).
+   */
+  guint idle_addins_load_source;
+
   /*
    * If we failed to build, this should be set.
    */
@@ -338,6 +339,12 @@ typedef struct
     struct {
       GPtrArray *stages;
     } clean;
+    struct {
+      GPtrArray *targets;
+    } build;
+    struct {
+      GPtrArray *targets;
+    } rebuild;
   };
 } TaskData;
 
@@ -410,6 +417,10 @@ task_data_free (gpointer data)
     {
       if (td->type == TASK_CLEAN)
         g_clear_pointer (&td->clean.stages, g_ptr_array_unref);
+      if (td->type == TASK_BUILD)
+        g_clear_pointer (&td->build.targets, g_ptr_array_unref);
+      if (td->type == TASK_REBUILD)
+        g_clear_pointer (&td->rebuild.targets, g_ptr_array_unref);
       td->type = 0;
       td->task = NULL;
       g_slice_free (TaskData, td);
@@ -499,8 +510,8 @@ create_diagnostic (IdeBuildPipeline *self,
   g_autofree gchar *column = NULL;
   g_autofree gchar *message = NULL;
   g_autofree gchar *level = NULL;
-  g_autoptr(IdeFile) file = NULL;
-  g_autoptr(IdeSourceLocation) location = NULL;
+  g_autoptr(GFile) file = NULL;
+  g_autoptr(IdeLocation) location = NULL;
   IdeContext *context;
   struct {
     gint64 line;
@@ -576,13 +587,10 @@ create_diagnostic (IdeBuildPipeline *self,
   if (!g_path_is_absolute (filename))
     {
       g_autoptr(GFile) child = NULL;
-      IdeVcs *vcs;
-      GFile *workdir;
+      g_autoptr(GFile) workdir = NULL;
       gchar *path;
 
-      vcs = ide_context_get_vcs (context);
-      workdir = ide_vcs_get_working_directory (vcs);
-
+      workdir = ide_context_ref_workdir (context);
       child = g_file_get_child (workdir, filename);
       path = g_file_get_path (child);
 
@@ -590,8 +598,8 @@ create_diagnostic (IdeBuildPipeline *self,
       filename = path;
     }
 
-  file = ide_file_new_for_path (context, filename);
-  location = ide_source_location_new (file, parsed.line, parsed.column, 0);
+  file = ide_context_build_file (context, filename);
+  location = ide_location_new (file, parsed.line, parsed.column);
 
   return ide_diagnostic_new (parsed.severity, message, location);
 }
@@ -666,7 +674,7 @@ extract_diagnostics (IdeBuildPipeline *self,
     {
       gsize out_len = 0;
 
-      unescaped = ide_build_utils_filter_color_codes (data, len, &out_len);
+      unescaped = _ide_build_utils_filter_color_codes (data, len, &out_len);
       if (out_len == 0)
         return;
 
@@ -722,11 +730,11 @@ ide_build_pipeline_log_observer (IdeBuildLogStream  stream,
 }
 
 static void
-ide_build_pipeline_intercept_pty_master_cb (const pty_intercept_t      *intercept,
-                                            const pty_intercept_side_t *side,
-                                            const guint8               *data,
-                                            gsize                       len,
-                                            gpointer                    user_data)
+ide_build_pipeline_intercept_pty_master_cb (const IdePtyIntercept     *intercept,
+                                            const IdePtyInterceptSide *side,
+                                            const guint8              *data,
+                                            gsize                      len,
+                                            gpointer                   user_data)
 {
   IdeBuildPipeline *self = user_data;
 
@@ -775,8 +783,8 @@ ide_build_pipeline_check_ready (IdeBuildPipeline *self,
   if (self->broken)
     {
       ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_FAILED,
+                                 IDE_BUILD_ERROR,
+                                 IDE_BUILD_ERROR_BROKEN,
                                  _("The build pipeline is in a failed state"));
       return FALSE;
     }
@@ -785,8 +793,8 @@ ide_build_pipeline_check_ready (IdeBuildPipeline *self,
     {
       /* configuration:ready is FALSE */
       ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_NOT_INITIALIZED,
+                                 IDE_BUILD_ERROR,
+                                 IDE_BUILD_ERROR_NOT_LOADED,
                                  _("The build configuration has errors"));
       return FALSE;
     }
@@ -921,43 +929,49 @@ ide_build_pipeline_real_finished (IdeBuildPipeline *self,
 }
 
 static void
-ide_build_pipeline_extension_added (PeasExtensionSet *set,
-                                    PeasPluginInfo   *plugin_info,
-                                    PeasExtension    *exten,
-                                    gpointer          user_data)
+ide_build_pipeline_extension_added (IdeExtensionSetAdapter *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 (IDE_IS_EXTENSION_SET_ADAPTER (set));
   g_assert (plugin_info != NULL);
   g_assert (IDE_IS_BUILD_PIPELINE_ADDIN (addin));
   g_assert (IDE_IS_BUILD_PIPELINE (self));
 
+  /* Mark that we loaded this addin, so we don't unload it if it
+   * was never loaded (during async loading).
+   */
+  g_object_set_data (G_OBJECT (addin), "HAS_LOADED", GINT_TO_POINTER (1));
+
   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)
+ide_build_pipeline_extension_removed (IdeExtensionSetAdapter *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 (IDE_IS_EXTENSION_SET_ADAPTER (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);
+  if (g_object_get_data (G_OBJECT (addin), "HAS_LOADED"))
+    ide_build_pipeline_addin_unload (addin, self);
 
   IDE_EXIT;
 }
@@ -965,6 +979,7 @@ ide_build_pipeline_extension_removed (PeasExtensionSet *set,
 static void
 build_command_query_cb (IdeBuildStage    *stage,
                         IdeBuildPipeline *pipeline,
+                        GPtrArray        *targets,
                         GCancellable     *cancellable,
                         gpointer          user_data)
 {
@@ -1021,13 +1036,12 @@ register_build_commands_stage (IdeBuildPipeline *self,
         ide_subprocess_launcher_set_cwd (launcher, rundir_path);
 
       stage = g_object_new (IDE_TYPE_BUILD_STAGE_LAUNCHER,
-                            "context", context,
                             "launcher", launcher,
                             NULL);
 
       g_signal_connect (stage, "query", G_CALLBACK (build_command_query_cb), NULL);
 
-      ide_build_pipeline_connect (self,
+      ide_build_pipeline_attach (self,
                                   IDE_BUILD_PHASE_BUILD | IDE_BUILD_PHASE_AFTER,
                                   i,
                                   stage);
@@ -1064,11 +1078,10 @@ register_post_install_commands_stage (IdeBuildPipeline *self,
       ide_subprocess_launcher_push_argv (launcher, post_install_commands[i]);
 
       stage = g_object_new (IDE_TYPE_BUILD_STAGE_LAUNCHER,
-                            "context", context,
                             "launcher", launcher,
                             NULL);
 
-      ide_build_pipeline_connect (self,
+      ide_build_pipeline_attach (self,
                                   IDE_BUILD_PHASE_INSTALL | IDE_BUILD_PHASE_AFTER,
                                   i,
                                   stage);
@@ -1076,14 +1089,14 @@ register_post_install_commands_stage (IdeBuildPipeline *self,
 }
 
 static void
-collect_pipeline_addins (PeasExtensionSet *set,
-                         PeasPluginInfo   *plugin_info,
-                         PeasExtension    *exten,
-                         gpointer          user_data)
+collect_pipeline_addins (IdeExtensionSetAdapter *set,
+                         PeasPluginInfo         *plugin_info,
+                         PeasExtension          *exten,
+                         gpointer                user_data)
 {
   GPtrArray *addins = user_data;
 
-  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
   g_assert (plugin_info != NULL);
   g_assert (IDE_IS_BUILD_PIPELINE_ADDIN (exten));
   g_assert (addins != NULL);
@@ -1124,6 +1137,7 @@ ide_build_pipeline_load_cb (IdleLoadState *state)
     }
 
   state->self->loaded = TRUE;
+  state->self->idle_addins_load_source = 0;
 
   return G_SOURCE_REMOVE;
 }
@@ -1151,15 +1165,17 @@ ide_build_pipeline_load (IdeBuildPipeline *self)
   g_assert (IDE_IS_BUILD_PIPELINE (self));
   g_assert (self->addins == NULL);
 
-  context = ide_object_get_context (IDE_OBJECT (self));
+  /* We might have already disposed if our pipeline got discarded */
+  if (!(context = ide_object_get_context (IDE_OBJECT (self))))
+    return;
 
   register_build_commands_stage (self, context);
   register_post_install_commands_stage (self, context);
 
-  self->addins = ide_extension_set_new (peas_engine_get_default (),
-                                        IDE_TYPE_BUILD_PIPELINE_ADDIN,
-                                        "context", context,
-                                        NULL);
+  self->addins = ide_extension_set_adapter_new (IDE_OBJECT (self),
+                                                peas_engine_get_default (),
+                                                IDE_TYPE_BUILD_PIPELINE_ADDIN,
+                                                NULL, NULL);
 
   g_signal_connect (self->addins,
                     "extension-added",
@@ -1175,18 +1191,19 @@ ide_build_pipeline_load (IdeBuildPipeline *self)
    * idle callback to reduce chances of stalling the main loop.
    */
   addins = g_ptr_array_new_with_free_func (g_object_unref);
-  peas_extension_set_foreach (self->addins,
-                              collect_pipeline_addins,
-                              addins);
+  ide_extension_set_adapter_foreach (self->addins,
+                                     collect_pipeline_addins,
+                                     addins);
 
   state = g_slice_new0 (IdleLoadState);
   state->self = g_object_ref (self);
   state->addins = g_steal_pointer (&addins);
 
-  g_idle_add_full (G_PRIORITY_LOW,
-                   (GSourceFunc) ide_build_pipeline_load_cb,
-                   state,
-                   idle_load_state_free);
+  self->idle_addins_load_source =
+    g_idle_add_full (G_PRIORITY_LOW,
+                     (GSourceFunc) ide_build_pipeline_load_cb,
+                     state,
+                     idle_load_state_free);
 
   IDE_EXIT;
 }
@@ -1334,13 +1351,15 @@ ide_build_pipeline_finalize (GObject *object)
 }
 
 static void
-ide_build_pipeline_dispose (GObject *object)
+ide_build_pipeline_destroy (IdeObject *object)
 {
   IdeBuildPipeline *self = IDE_BUILD_PIPELINE (object);
-  g_auto(pty_fd_t) fd = PTY_FD_INVALID;
+  g_auto(IdePtyFd) fd = IDE_PTY_FD_INVALID;
 
   IDE_ENTRY;
 
+  g_clear_handle_id (&self->idle_addins_load_source, g_source_remove);
+
   _ide_build_pipeline_cancel (self);
 
   ide_build_pipeline_unload (self);
@@ -1350,10 +1369,10 @@ ide_build_pipeline_dispose (GObject *object)
   g_clear_object (&self->pty);
   fd = pty_fd_steal (&self->pty_slave);
 
-  if (PTY_IS_INTERCEPT (&self->intercept))
-    pty_intercept_clear (&self->intercept);
+  if (IDE_IS_PTY_INTERCEPT (&self->intercept))
+    ide_pty_intercept_clear (&self->intercept);
 
-  G_OBJECT_CLASS (ide_build_pipeline_parent_class)->dispose (object);
+  IDE_OBJECT_CLASS (ide_build_pipeline_parent_class)->destroy (object);
 
   IDE_EXIT;
 }
@@ -1364,7 +1383,7 @@ ide_build_pipeline_initable_init (GInitable     *initable,
                                   GError       **error)
 {
   IdeBuildPipeline *self = (IdeBuildPipeline *)initable;
-  pty_fd_t master_fd;
+  IdePtyFd master_fd;
 
   IDE_ENTRY;
 
@@ -1396,7 +1415,7 @@ ide_build_pipeline_initable_init (GInitable     *initable,
 
   master_fd = vte_pty_get_fd (self->pty);
 
-  if (!pty_intercept_init (&self->intercept, master_fd, NULL))
+  if (!ide_pty_intercept_init (&self->intercept, master_fd, NULL))
     {
       g_set_error_literal (error,
                            G_IO_ERROR,
@@ -1405,7 +1424,7 @@ ide_build_pipeline_initable_init (GInitable     *initable,
       IDE_RETURN (FALSE);
     }
 
-  pty_intercept_set_callback (&self->intercept,
+  ide_pty_intercept_set_callback (&self->intercept,
                               &self->intercept.master,
                               ide_build_pipeline_intercept_pty_master_cb,
                               self);
@@ -1430,27 +1449,29 @@ initable_iface_init (GInitableIface *iface)
 }
 
 static void
-ide_build_pipeline_constructed (GObject *object)
+ide_build_pipeline_parent_set (IdeObject *object,
+                               IdeObject *parent)
 {
   IdeBuildPipeline *self = IDE_BUILD_PIPELINE (object);
-  IdeContext *context;
-  IdeVcs *vcs;
-  GFile *workdir;
   IdeToolchainManager *toolchain_manager;
+  g_autoptr(IdeContext) context = NULL;
+  g_autoptr(GFile) workdir = NULL;
 
   IDE_ENTRY;
 
-  G_OBJECT_CLASS (ide_build_pipeline_parent_class)->constructed (object);
-
+  g_assert (IDE_IS_BUILD_PIPELINE (self));
+  g_assert (!parent || IDE_IS_OBJECT (parent));
   g_assert (IDE_IS_CONFIGURATION (self->configuration));
 
-  context = ide_object_get_context (IDE_OBJECT (self));
-  vcs = ide_context_get_vcs (context);
-  workdir = ide_vcs_get_working_directory (vcs);
+  if (parent == NULL)
+    return;
+
+  context = IDE_CONTEXT (ide_object_ref_root (IDE_OBJECT (self)));
+  workdir = ide_context_ref_workdir (context);
 
   self->srcdir = g_file_get_path (workdir);
 
-  toolchain_manager = ide_context_get_toolchain_manager (context);
+  toolchain_manager = ide_toolchain_manager_from_context (context);
   self->toolchain = ide_toolchain_manager_get_toolchain (toolchain_manager, "default");
 
   IDE_EXIT;
@@ -1518,13 +1539,15 @@ static void
 ide_build_pipeline_class_init (IdeBuildPipelineClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeObjectClass *i_object_class = IDE_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;
 
+  i_object_class->destroy = ide_build_pipeline_destroy;
+  i_object_class->parent_set = ide_build_pipeline_parent_set;
+
   /**
    * IdeBuildPipeline:busy:
    *
@@ -1877,8 +1900,15 @@ ide_build_pipeline_tick_execute (IdeBuildPipeline *self,
 
       if ((entry->phase & IDE_BUILD_PHASE_MASK) & self->requested_mask)
         {
+          GPtrArray *targets = NULL;
+
           self->current_stage = entry->stage;
 
+          if (td->type == TASK_BUILD)
+            targets = td->build.targets;
+          else if (td->type == TASK_REBUILD)
+            targets = td->rebuild.targets;
+
           /*
            * We might be able to chain upcoming stages to this stage and avoid
            * duplicate work. This will also advance self->position based on
@@ -1888,6 +1918,7 @@ ide_build_pipeline_tick_execute (IdeBuildPipeline *self,
 
           _ide_build_stage_execute_with_query_async (entry->stage,
                                                      self,
+                                                     targets,
                                                      cancellable,
                                                      ide_build_pipeline_stage_execute_cb,
                                                      g_object_ref (task));
@@ -1950,9 +1981,11 @@ ide_build_pipeline_task_notify_completed (IdeBuildPipeline *self,
 }
 
 /**
- * ide_build_pipeline_build_async:
+ * ide_build_pipeline_build_targets_async:
  * @self: A @IdeBuildPipeline
  * @phase: the requested build phase
+ * @targets: (nullable) (element-type IdeBuildTarget): an optional array of
+ *   #IdeBuildTarget for the pipeline to build.
  * @cancellable: (nullable): a #GCancellable or %NULL
  * @callback: a callback to execute upon completion
  * @user_data: data for @callback
@@ -1976,11 +2009,12 @@ ide_build_pipeline_task_notify_completed (IdeBuildPipeline *self,
  * Since: 3.32
  */
 void
-ide_build_pipeline_build_async (IdeBuildPipeline    *self,
-                                IdeBuildPhase        phase,
-                                GCancellable        *cancellable,
-                                GAsyncReadyCallback  callback,
-                                gpointer             user_data)
+ide_build_pipeline_build_targets_async (IdeBuildPipeline    *self,
+                                        IdeBuildPhase        phase,
+                                        GPtrArray           *targets,
+                                        GCancellable        *cancellable,
+                                        GAsyncReadyCallback  callback,
+                                        gpointer             user_data)
 {
   g_autoptr(IdeTask) task = NULL;
   TaskData *task_data;
@@ -2028,6 +2062,7 @@ ide_build_pipeline_build_async (IdeBuildPipeline    *self,
 
   task_data = task_data_new (task, TASK_BUILD);
   task_data->phase = 1 << g_bit_nth_msf (phase, -1);
+  task_data->build.targets = _g_ptr_array_copy_objects (targets);
   ide_task_set_task_data (task, task_data, task_data_free);
 
   g_queue_push_tail (&self->task_queue, g_steal_pointer (&task));
@@ -2041,6 +2076,74 @@ short_circuit:
   IDE_EXIT;
 }
 
+/**
+ * ide_build_pipeline_build_targets_finish:
+ * @self: An #IdeBuildPipeline
+ * @result: a #GAsyncResult provided to callback
+ * @error: A location for a #GError, or %NULL
+ *
+ * This function completes the asynchronous request to build
+ * up to a particular phase and targets of the build pipeline.
+ *
+ * Returns: %TRUE if the build stages were executed successfully
+ *   up to the requested build phase provided to
+ *   ide_build_pipeline_build_targets_async().
+ *
+ * Since: 3.32
+ */
+gboolean
+ide_build_pipeline_build_targets_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 (IDE_IS_TASK (result), FALSE);
+
+  ret = ide_task_propagate_boolean (IDE_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+/**
+ * ide_build_pipeline_build_async:
+ * @self: A @IdeBuildPipeline
+ * @phase: the requested build phase
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: a callback to execute upon completion
+ * @user_data: data for @callback
+ *
+ * Asynchronously starts the build pipeline.
+ *
+ * The @phase parameter should contain the #IdeBuildPhase that is
+ * necessary to complete. If you simply want to trigger a generic
+ * build, you probably want %IDE_BUILD_PHASE_BUILD. If you only
+ * need to configure the project (and necessarily the dependencies
+ * up to that phase) you might want %IDE_BUILD_PHASE_CONFIGURE.
+ *
+ * You may not specify %IDE_BUILD_PHASE_AFTER or
+ * %IDE_BUILD_PHASE_BEFORE flags as those must always be processed
+ * with the underlying phase they are attached to.
+ *
+ * Upon completion, @callback will be executed and should call
+ * ide_build_pipeline_execute_finish() to get the status of the
+ * operation.
+ *
+ * Since: 3.32
+ */
+void
+ide_build_pipeline_build_async (IdeBuildPipeline    *self,
+                                IdeBuildPhase        phase,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  ide_build_pipeline_build_targets_async (self, phase, NULL, cancellable, callback, user_data);
+}
+
 /**
  * ide_build_pipeline_build_finish:
  * @self: An #IdeBuildPipeline
@@ -2188,8 +2291,8 @@ ide_build_pipeline_do_flush (gpointer data)
       task_data->phase <= IDE_BUILD_PHASE_CONFIGURE)
     {
       ide_task_return_new_error (task,
-                                 G_IO_ERROR,
-                                 G_IO_ERROR_FAILED,
+                                 IDE_BUILD_ERROR,
+                                 IDE_BUILD_ERROR_NEEDS_REBUILD,
                                  "The build pipeline is in a failed state and requires a rebuild");
       IDE_RETURN (G_SOURCE_REMOVE);
     }
@@ -2314,7 +2417,7 @@ ide_build_pipeline_execute_finish (IdeBuildPipeline  *self,
 }
 
 /**
- * ide_build_pipeline_connect:
+ * ide_build_pipeline_attach:
  * @self: an #IdeBuildPipeline
  * @phase: An #IdeBuildPhase
  * @priority: an optional priority for sorting within the phase
@@ -2325,12 +2428,12 @@ ide_build_pipeline_execute_finish (IdeBuildPipeline  *self,
  * If priority is non-zero, it will be used to sort the stage among other
  * stages that are part of the same phase.
  *
- * Returns: A stage_id that may be passed to ide_build_pipeline_disconnect().
+ * Returns: A stage_id that may be passed to ide_build_pipeline_detach().
  *
  * Since: 3.32
  */
 guint
-ide_build_pipeline_connect (IdeBuildPipeline *self,
+ide_build_pipeline_attach (IdeBuildPipeline *self,
                             IdeBuildPhase     phase,
                             gint              priority,
                             IdeBuildStage    *stage)
@@ -2395,6 +2498,8 @@ ide_build_pipeline_connect (IdeBuildPipeline *self,
                 }
             }
 
+          ide_object_append (IDE_OBJECT (self), IDE_OBJECT (stage));
+
           IDE_GOTO (cleanup);
         }
     }
@@ -2409,7 +2514,7 @@ cleanup:
 }
 
 /**
- * ide_build_pipeline_connect_launcher:
+ * ide_build_pipeline_attach_launcher:
  * @self: an #IdeBuildPipeline
  * @phase: An #IdeBuildPhase
  * @priority: an optional priority for sorting within the phase
@@ -2426,7 +2531,7 @@ cleanup:
  * Since: 3.32
  */
 guint
-ide_build_pipeline_connect_launcher (IdeBuildPipeline      *self,
+ide_build_pipeline_attach_launcher (IdeBuildPipeline      *self,
                                      IdeBuildPhase          phase,
                                      gint                   priority,
                                      IdeSubprocessLauncher *launcher)
@@ -2443,7 +2548,7 @@ ide_build_pipeline_connect_launcher (IdeBuildPipeline      *self,
   context = ide_object_get_context (IDE_OBJECT (self));
   stage = ide_build_stage_launcher_new (context, launcher);
 
-  return ide_build_pipeline_connect (self, phase, priority, stage);
+  return ide_build_pipeline_attach (self, phase, priority, stage);
 }
 
 /**
@@ -2651,13 +2756,13 @@ ide_build_pipeline_build_builddir_path (IdeBuildPipeline *self,
 }
 
 /**
- * ide_build_pipeline_disconnect:
+ * ide_build_pipeline_detach:
  * @self: An #IdeBuildPipeline
  * @stage_id: An identifier returned from adding a stage
  *
  * This removes the stage matching @stage_id. You are returned a @stage_id when
- * inserting a stage with functions such as ide_build_pipeline_connect()
- * or ide_build_pipeline_connect_launcher().
+ * inserting a stage with functions such as ide_build_pipeline_attach()
+ * or ide_build_pipeline_attach_launcher().
  *
  * Plugins should use this function to remove their stages when the plugin
  * is unloading.
@@ -2665,7 +2770,7 @@ ide_build_pipeline_build_builddir_path (IdeBuildPipeline *self,
  * Since: 3.32
  */
 void
-ide_build_pipeline_disconnect (IdeBuildPipeline *self,
+ide_build_pipeline_detach (IdeBuildPipeline *self,
                                guint             stage_id)
 {
   g_return_if_fail (IDE_IS_BUILD_PIPELINE (self));
@@ -2678,6 +2783,7 @@ ide_build_pipeline_disconnect (IdeBuildPipeline *self,
 
       if (entry->id == stage_id)
         {
+          ide_object_destroy (IDE_OBJECT (entry->stage));
           g_array_remove_index (self->pipeline, i);
           g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
           break;
@@ -2723,7 +2829,7 @@ ide_build_pipeline_invalidate_phase (IdeBuildPipeline *self,
  * @stage_id: the identfier of the stage
  *
  * Gets the stage matching the identifier @stage_id as returned from
- * ide_build_pipeline_connect().
+ * ide_build_pipeline_attach().
  *
  * Returns: (transfer none) (nullable): An #IdeBuildStage or %NULL if the
  *   stage could not be found.
@@ -2857,8 +2963,8 @@ ide_build_pipeline_attach_pty (IdeBuildPipeline      *self,
 
   if (self->pty_slave == -1)
     {
-      pty_fd_t master_fd = pty_intercept_get_fd (&self->intercept);
-      self->pty_slave = pty_intercept_create_slave (master_fd, TRUE);
+      IdePtyFd master_fd = ide_pty_intercept_get_fd (&self->intercept);
+      self->pty_slave = ide_pty_intercept_create_slave (master_fd, TRUE);
     }
 
   if (self->pty_slave == -1)
@@ -3081,7 +3187,7 @@ ide_build_pipeline_get_message (IdeBuildPipeline *self)
     {
       const gchar *name = ide_build_stage_get_name (self->current_stage);
 
-      if (!dzl_str_empty0 (name))
+      if (!ide_str_empty0 (name))
         return g_strdup (name);
     }
 
@@ -3530,9 +3636,25 @@ ide_build_pipeline_tick_rebuild (IdeBuildPipeline *self,
   IDE_EXIT;
 }
 
+/**
+ * ide_build_pipeline_rebuild_async:
+ * @self: A @IdeBuildPipeline
+ * @phase: the requested build phase
+ * @targets: (element-type IdeBuildTarget) (nullable): an array of
+ *   #IdeBuildTarget or %NULL
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: a callback to execute upon completion
+ * @user_data: data for @callback
+ *
+ * Asynchronously starts the build pipeline after cleaning any
+ * existing build artifacts.
+ *
+ * Since: 3.32
+ */
 void
 ide_build_pipeline_rebuild_async (IdeBuildPipeline    *self,
                                   IdeBuildPhase        phase,
+                                  GPtrArray           *targets,
                                   GCancellable        *cancellable,
                                   GAsyncReadyCallback  callback,
                                   gpointer             user_data)
@@ -3557,6 +3679,7 @@ ide_build_pipeline_rebuild_async (IdeBuildPipeline    *self,
 
   td = task_data_new (task, TASK_REBUILD);
   td->phase = phase;
+  td->rebuild.targets = _g_ptr_array_copy_objects (targets);
   ide_task_set_task_data (task, td, task_data_free);
 
   g_queue_push_tail (&self->task_queue, g_steal_pointer (&task));
@@ -3635,7 +3758,7 @@ _ide_build_pipeline_set_message (IdeBuildPipeline *self,
         message += strlen ("jhbuild:");
     }
 
-  if (!dzl_str_equal0 (message, self->message))
+  if (!ide_str_equal0 (message, self->message))
     {
       g_free (self->message);
       self->message = g_strdup (message);
@@ -3800,8 +3923,8 @@ _ide_build_pipeline_set_pty_size (IdeBuildPipeline *self,
 {
   g_return_if_fail (IDE_IS_BUILD_PIPELINE (self));
 
-  if (self->pty_slave != PTY_FD_INVALID)
-    pty_intercept_set_size (&self->intercept, rows, columns);
+  if (self->pty_slave != IDE_PTY_FD_INVALID)
+    ide_pty_intercept_set_size (&self->intercept, rows, columns);
 }
 
 void
@@ -3817,7 +3940,7 @@ _ide_build_pipeline_set_runtime (IdeBuildPipeline *self,
       IdeContext *context;
 
       context = ide_object_get_context (IDE_OBJECT (self));
-      build_system = ide_context_get_build_system (context);
+      build_system = ide_build_system_from_context (context);
 
       g_clear_pointer (&self->builddir, g_free);
       self->builddir = ide_build_system_get_builddir (build_system, self);
@@ -3831,8 +3954,34 @@ _ide_build_pipeline_set_toolchain (IdeBuildPipeline *self,
   g_return_if_fail (IDE_IS_BUILD_PIPELINE (self));
   g_return_if_fail (!toolchain || IDE_IS_TOOLCHAIN (toolchain));
 
+  ide_object_lock (IDE_OBJECT (self));
   if (g_set_object (&self->toolchain, toolchain))
     ide_configuration_set_toolchain (self->configuration, toolchain);
+  ide_object_unlock (IDE_OBJECT (self));
+}
+
+/**
+ * ide_build_pipeline_ref_toolchain:
+ * @self: a #IdeBuildPipeline
+ *
+ * Thread-safe variant of ide_build_pipeline_get_toolchain().
+ *
+ * Returns: (transfer full) (nullable): an #IdeToolchain or %NULL
+ *
+ * Since: 3.32
+ */
+IdeToolchain *
+ide_build_pipeline_ref_toolchain (IdeBuildPipeline *self)
+{
+  IdeToolchain *ret = NULL;
+
+  g_return_val_if_fail (IDE_IS_BUILD_PIPELINE (self), NULL);
+
+  ide_object_lock (IDE_OBJECT (self));
+  g_set_object (&ret, self->toolchain);
+  ide_object_unlock (IDE_OBJECT (self));
+
+  return g_steal_pointer (&ret);
 }
 
 void
@@ -3854,7 +4003,7 @@ _ide_build_pipeline_check_toolchain (IdeBuildPipeline *self,
   context = ide_object_get_context (IDE_OBJECT (self));
   g_return_if_fail (IDE_IS_CONTEXT (context));
 
-  manager = ide_context_get_toolchain_manager (context);
+  manager = ide_toolchain_manager_from_context (context);
   g_return_if_fail (IDE_IS_TOOLCHAIN_MANAGER (manager));
 
   toolchain = ide_configuration_get_toolchain (self->configuration);
diff --git a/src/libide/buildsystem/ide-build-pipeline.h b/src/libide/foundry/ide-build-pipeline.h
similarity index 84%
rename from src/libide/buildsystem/ide-build-pipeline.h
rename to src/libide/foundry/ide-build-pipeline.h
index 98b3623a6..37065956a 100644
--- a/src/libide/buildsystem/ide-build-pipeline.h
+++ b/src/libide/foundry/ide-build-pipeline.h
@@ -20,24 +20,29 @@
 
 #pragma once
 
-#include <gio/gio.h>
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+#include <libide-code.h>
+#include <libide-threading.h>
 #include <vte/vte.h>
 
-#include "ide-types.h"
-#include "ide-version-macros.h"
+#include "ide-foundry-types.h"
 
-#include "buildsystem/ide-build-log.h"
-#include "buildsystem/ide-build-stage.h"
-#include "config/ide-configuration.h"
-#include "runtimes/ide-runtime.h"
-#include "subprocess/ide-subprocess-launcher.h"
-#include "util/ide-triplet.h"
+#include "ide-build-log.h"
+#include "ide-build-stage.h"
+#include "ide-configuration.h"
+#include "ide-runtime.h"
+#include "ide-triplet.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)
+#define IDE_BUILD_ERROR             (ide_build_error_quark())
 
 typedef enum
 {
@@ -58,9 +63,19 @@ typedef enum
   IDE_BUILD_PHASE_FAILED       = 1 << 31,
 } IdeBuildPhase;
 
+typedef enum
+{
+  IDE_BUILD_ERROR_UNKNOWN = 0,
+  IDE_BUILD_ERROR_BROKEN,
+  IDE_BUILD_ERROR_NOT_LOADED,
+  IDE_BUILD_ERROR_NEEDS_REBUILD,
+} IdeBuildError;
+
 IDE_AVAILABLE_IN_3_32
 G_DECLARE_FINAL_TYPE (IdeBuildPipeline, ide_build_pipeline, IDE, BUILD_PIPELINE, IdeObject)
 
+IDE_AVAILABLE_IN_3_32
+GQuark                 ide_build_error_quark                      (void) G_GNUC_CONST;
 IDE_AVAILABLE_IN_3_32
 gboolean               ide_build_pipeline_is_native               (IdeBuildPipeline       *self);
 IDE_AVAILABLE_IN_3_32
@@ -78,6 +93,8 @@ IdeRuntime            *ide_build_pipeline_get_runtime             (IdeBuildPipel
 IDE_AVAILABLE_IN_3_32
 IdeToolchain          *ide_build_pipeline_get_toolchain           (IdeBuildPipeline       *self);
 IDE_AVAILABLE_IN_3_32
+IdeToolchain          *ide_build_pipeline_ref_toolchain           (IdeBuildPipeline       *self);
+IDE_AVAILABLE_IN_3_32
 const gchar           *ide_build_pipeline_get_builddir            (IdeBuildPipeline       *self);
 IDE_AVAILABLE_IN_3_32
 const gchar           *ide_build_pipeline_get_srcdir              (IdeBuildPipeline       *self);
@@ -107,17 +124,17 @@ IDE_AVAILABLE_IN_3_32
 gboolean               ide_build_pipeline_request_phase           (IdeBuildPipeline       *self,
                                                                    IdeBuildPhase           phase);
 IDE_AVAILABLE_IN_3_32
-guint                  ide_build_pipeline_connect                 (IdeBuildPipeline       *self,
+guint                  ide_build_pipeline_attach                 (IdeBuildPipeline       *self,
                                                                    IdeBuildPhase           phase,
                                                                    gint                    priority,
                                                                    IdeBuildStage          *stage);
 IDE_AVAILABLE_IN_3_32
-guint                  ide_build_pipeline_connect_launcher        (IdeBuildPipeline       *self,
+guint                  ide_build_pipeline_attach_launcher        (IdeBuildPipeline       *self,
                                                                    IdeBuildPhase           phase,
                                                                    gint                    priority,
                                                                    IdeSubprocessLauncher  *launcher);
 IDE_AVAILABLE_IN_3_32
-void                   ide_build_pipeline_disconnect              (IdeBuildPipeline       *self,
+void                   ide_build_pipeline_detach              (IdeBuildPipeline       *self,
                                                                    guint                   stage_id);
 IDE_AVAILABLE_IN_3_32
 IdeBuildStage         *ide_build_pipeline_get_stage_by_id         (IdeBuildPipeline       *self,
@@ -151,6 +168,17 @@ gboolean               ide_build_pipeline_build_finish            (IdeBuildPipel
                                                                    GAsyncResult           *result,
                                                                    GError                **error);
 IDE_AVAILABLE_IN_3_32
+void                   ide_build_pipeline_build_targets_async     (IdeBuildPipeline       *self,
+                                                                   IdeBuildPhase           phase,
+                                                                   GPtrArray              *targets,
+                                                                   GCancellable           *cancellable,
+                                                                   GAsyncReadyCallback     callback,
+                                                                   gpointer                user_data);
+IDE_AVAILABLE_IN_3_32
+gboolean               ide_build_pipeline_build_targets_finish    (IdeBuildPipeline       *self,
+                                                                   GAsyncResult           *result,
+                                                                   GError                **error);
+IDE_AVAILABLE_IN_3_32
 void                   ide_build_pipeline_execute_async           (IdeBuildPipeline       *self,
                                                                    GCancellable           *cancellable,
                                                                    GAsyncReadyCallback     callback,
@@ -176,6 +204,7 @@ gboolean               ide_build_pipeline_clean_finish            (IdeBuildPipel
 IDE_AVAILABLE_IN_3_32
 void                   ide_build_pipeline_rebuild_async           (IdeBuildPipeline       *self,
                                                                    IdeBuildPhase           phase,
+                                                                   GPtrArray              *targets,
                                                                    GCancellable           *cancellable,
                                                                    GAsyncReadyCallback     callback,
                                                                    gpointer                user_data);
diff --git a/src/libide/buildsystem/ide-build-private.h b/src/libide/foundry/ide-build-private.h
similarity index 88%
rename from src/libide/buildsystem/ide-build-private.h
rename to src/libide/foundry/ide-build-private.h
index c9bbb0861..a9360e952 100644
--- a/src/libide/buildsystem/ide-build-private.h
+++ b/src/libide/foundry/ide-build-private.h
@@ -22,12 +22,13 @@
 
 #include <vte/vte.h>
 
-#include "buildsystem/ide-build-pipeline.h"
-#include "devices/ide-device-info.h"
-#include "runtimes/ide-runtime.h"
+#include "ide-foundry-types.h"
 
 G_BEGIN_DECLS
 
+guint8 *_ide_build_utils_filter_color_codes (const guint8 *data,
+                                             gsize         len,
+                                             gsize        *out_len);
 void _ide_build_pipeline_cancel          (IdeBuildPipeline *self);
 void _ide_build_pipeline_set_runtime     (IdeBuildPipeline *self,
                                           IdeRuntime       *runtime);
diff --git a/src/libide/buildsystem/ide-build-stage-launcher.c b/src/libide/foundry/ide-build-stage-launcher.c
similarity index 98%
rename from src/libide/buildsystem/ide-build-stage-launcher.c
rename to src/libide/foundry/ide-build-stage-launcher.c
index 6fc4129ed..05786617a 100644
--- a/src/libide/buildsystem/ide-build-stage-launcher.c
+++ b/src/libide/foundry/ide-build-stage-launcher.c
@@ -22,13 +22,11 @@
 
 #include "config.h"
 
-#include "ide-debug.h"
+#include <libide-threading.h>
 
-#include "buildsystem/ide-build-log.h"
-#include "buildsystem/ide-build-pipeline.h"
-#include "buildsystem/ide-build-stage-launcher.h"
-#include "subprocess/ide-subprocess.h"
-#include "threading/ide-task.h"
+#include "ide-build-log.h"
+#include "ide-build-pipeline.h"
+#include "ide-build-stage-launcher.h"
 
 typedef struct
 {
@@ -514,7 +512,6 @@ 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/src/libide/buildsystem/ide-build-stage-launcher.h b/src/libide/foundry/ide-build-stage-launcher.h
similarity index 91%
rename from src/libide/buildsystem/ide-build-stage-launcher.h
rename to src/libide/foundry/ide-build-stage-launcher.h
index aa46c86dd..e454343e7 100644
--- a/src/libide/buildsystem/ide-build-stage-launcher.h
+++ b/src/libide/foundry/ide-build-stage-launcher.h
@@ -20,14 +20,14 @@
 
 #pragma once
 
-#include <gio/gio.h>
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <libide-core.h>
+#include <libide-threading.h>
 
-#include "ide-context.h"
-
-#include "buildsystem/ide-build-stage.h"
-#include "subprocess/ide-subprocess-launcher.h"
+#include "ide-build-stage.h"
 
 G_BEGIN_DECLS
 
@@ -41,10 +41,7 @@ struct _IdeBuildStageLauncherClass
   IdeBuildStageClass parent_class;
 
   /*< private >*/
-  gpointer _reserved1;
-  gpointer _reserved2;
-  gpointer _reserved3;
-  gpointer _reserved4;
+  gpointer _reserved[8];
 };
 
 IDE_AVAILABLE_IN_3_32
diff --git a/src/libide/buildsystem/ide-build-stage-mkdirs.c b/src/libide/foundry/ide-build-stage-mkdirs.c
similarity index 97%
rename from src/libide/buildsystem/ide-build-stage-mkdirs.c
rename to src/libide/foundry/ide-build-stage-mkdirs.c
index e5cc2c259..900300c7b 100644
--- a/src/libide/buildsystem/ide-build-stage-mkdirs.c
+++ b/src/libide/foundry/ide-build-stage-mkdirs.c
@@ -29,10 +29,8 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include "ide-debug.h"
-
-#include "buildsystem/ide-build-pipeline.h"
-#include "buildsystem/ide-build-stage-mkdirs.h"
+#include "ide-build-pipeline.h"
+#include "ide-build-stage-mkdirs.h"
 
 typedef struct
 {
@@ -60,6 +58,7 @@ clear_path (gpointer data)
 static void
 ide_build_stage_mkdirs_query (IdeBuildStage    *stage,
                               IdeBuildPipeline *pipeline,
+                              GPtrArray        *targets,
                               GCancellable     *cancellable)
 {
   IdeBuildStageMkdirs *self = (IdeBuildStageMkdirs *)stage;
@@ -198,7 +197,6 @@ IdeBuildStage *
 ide_build_stage_mkdirs_new (IdeContext *context)
 {
   return g_object_new (IDE_TYPE_BUILD_STAGE_MKDIRS,
-                       "context", context,
                        NULL);
 }
 
diff --git a/src/libide/buildsystem/ide-build-stage-mkdirs.h b/src/libide/foundry/ide-build-stage-mkdirs.h
similarity index 88%
rename from src/libide/buildsystem/ide-build-stage-mkdirs.h
rename to src/libide/foundry/ide-build-stage-mkdirs.h
index 38dd1fc76..c8bb3d6f4 100644
--- a/src/libide/buildsystem/ide-build-stage-mkdirs.h
+++ b/src/libide/foundry/ide-build-stage-mkdirs.h
@@ -20,9 +20,13 @@
 
 #pragma once
 
-#include "ide-version-macros.h"
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
 
-#include "buildsystem/ide-build-stage.h"
+#include <libide-core.h>
+
+#include "ide-build-stage.h"
 
 G_BEGIN_DECLS
 
@@ -36,10 +40,7 @@ struct _IdeBuildStageMkdirsClass
   IdeBuildStageClass parent_class;
 
   /*< private >*/
-  gpointer _reserved1;
-  gpointer _reserved2;
-  gpointer _reserved3;
-  gpointer _reserved4;
+  gpointer _reserved[8];
 };
 
 IDE_AVAILABLE_IN_3_32
diff --git a/src/libide/buildsystem/ide-build-stage-private.h b/src/libide/foundry/ide-build-stage-private.h
similarity index 94%
rename from src/libide/buildsystem/ide-build-stage-private.h
rename to src/libide/foundry/ide-build-stage-private.h
index 3f1d1bead..ec203c35e 100644
--- a/src/libide/buildsystem/ide-build-stage-private.h
+++ b/src/libide/foundry/ide-build-stage-private.h
@@ -20,8 +20,7 @@
 
 #pragma once
 
-#include "buildsystem/ide-build-pipeline.h"
-#include "buildsystem/ide-build-stage.h"
+#include "ide-foundry-types.h"
 
 G_BEGIN_DECLS
 
@@ -31,6 +30,7 @@ void          _ide_build_stage_set_phase                 (IdeBuildStage        *
                                                           IdeBuildPhase         phase);
 void          _ide_build_stage_execute_with_query_async  (IdeBuildStage        *self,
                                                           IdeBuildPipeline     *pipeline,
+                                                          GPtrArray            *targets,
                                                           GCancellable         *cancellable,
                                                           GAsyncReadyCallback   callback,
                                                           gpointer              user_data);
diff --git a/src/libide/buildsystem/ide-build-stage-transfer.c b/src/libide/foundry/ide-build-stage-transfer.c
similarity index 85%
rename from src/libide/buildsystem/ide-build-stage-transfer.c
rename to src/libide/foundry/ide-build-stage-transfer.c
index ad68bc5e9..c27e46b24 100644
--- a/src/libide/buildsystem/ide-build-stage-transfer.c
+++ b/src/libide/foundry/ide-build-stage-transfer.c
@@ -22,17 +22,12 @@
 
 #include "config.h"
 
+#include <libide-threading.h>
 #include <glib/gi18n.h>
 
-#include "ide-context.h"
-#include "ide-debug.h"
-
-#include "application/ide-application.h"
-#include "buildsystem/ide-build-stage-transfer.h"
-#include "buildsystem/ide-build-pipeline.h"
-#include "transfers/ide-transfer-manager.h"
-#include "transfers/ide-transfer.h"
-#include "threading/ide-task.h"
+#include "ide-build-stage-transfer.h"
+#include "ide-build-pipeline.h"
+#include "ide-transfer.h"
 
 struct _IdeBuildStageTransfer
 {
@@ -60,11 +55,17 @@ ide_build_stage_transfer_execute_cb (GObject      *object,
   IdeTransferManager *transfer_manager = (IdeTransferManager *)object;
   g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
+  IdeNotification *notif;
 
   g_assert (IDE_IS_TRANSFER_MANAGER (transfer_manager));
   g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (IDE_IS_TASK (task));
 
+  notif = ide_task_get_task_data (task);
+  g_assert (IDE_IS_NOTIFICATION (notif));
+
+  ide_notification_withdraw (notif);
+
   if (!ide_transfer_manager_execute_finish (transfer_manager, result, &error))
     ide_task_return_error (task, g_steal_pointer (&error));
   else
@@ -90,8 +91,11 @@ ide_build_stage_transfer_execute_async (IdeBuildStage       *stage,
                                         gpointer             user_data)
 {
   IdeBuildStageTransfer *self = (IdeBuildStageTransfer *)stage;
+  g_autoptr(IdeNotification) notif = NULL;
+  g_autoptr(IdeNotifications) notifs = NULL;
   g_autoptr(IdeTask) task = NULL;
-  IdeTransferManager *transfer_manager;
+  g_autoptr(IdeContext) context = NULL;
+  const gchar *icon_name;
 
   IDE_ENTRY;
 
@@ -135,9 +139,25 @@ ide_build_stage_transfer_execute_async (IdeBuildStage       *stage,
         }
     }
 
-  transfer_manager = ide_application_get_transfer_manager (IDE_APPLICATION_DEFAULT);
+  notif = ide_notification_new ();
+  ide_notification_set_has_progress (notif, TRUE);
+  g_object_bind_property (self->transfer, "title", notif, "title", G_BINDING_SYNC_CREATE);
+  g_object_bind_property (self->transfer, "status", notif, "body", G_BINDING_SYNC_CREATE);
+  g_object_bind_property (self->transfer, "progress", notif, "progress", G_BINDING_SYNC_CREATE);
+
+  if ((icon_name = ide_transfer_get_icon_name (self->transfer)))
+    {
+      g_autoptr(GIcon) icon = g_themed_icon_new (icon_name);
+      ide_notification_set_icon (notif, icon);
+    }
+
+  context = ide_object_ref_context (IDE_OBJECT (self));
+  notifs = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_NOTIFICATIONS);
+  ide_notifications_add_notification (notifs, notif);
+
+  ide_task_set_task_data (task, g_steal_pointer (&notif), g_object_unref);
 
-  ide_transfer_manager_execute_async (transfer_manager,
+  ide_transfer_manager_execute_async (NULL,
                                       self->transfer,
                                       cancellable,
                                       ide_build_stage_transfer_execute_cb,
diff --git a/src/libide/buildsystem/ide-build-stage-transfer.h b/src/libide/foundry/ide-build-stage-transfer.h
similarity index 86%
rename from src/libide/buildsystem/ide-build-stage-transfer.h
rename to src/libide/foundry/ide-build-stage-transfer.h
index 250e38ae0..bb6ee2304 100644
--- a/src/libide/buildsystem/ide-build-stage-transfer.h
+++ b/src/libide/foundry/ide-build-stage-transfer.h
@@ -20,9 +20,13 @@
 
 #pragma once
 
-#include "ide-version-macros.h"
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
 
-#include "buildsystem/ide-build-stage.h"
+#include <libide-core.h>
+
+#include "ide-build-stage.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/buildsystem/ide-build-stage.c b/src/libide/foundry/ide-build-stage.c
similarity index 93%
rename from src/libide/buildsystem/ide-build-stage.c
rename to src/libide/foundry/ide-build-stage.c
index 5766469a5..872ca4c74 100644
--- a/src/libide/buildsystem/ide-build-stage.c
+++ b/src/libide/foundry/ide-build-stage.c
@@ -22,15 +22,12 @@
 
 #include "config.h"
 
+#include <libide-threading.h>
 #include <string.h>
 
-#include "ide-debug.h"
-
-#include "buildsystem/ide-build-pipeline.h"
-#include "buildsystem/ide-build-stage.h"
-#include "buildsystem/ide-build-stage-private.h"
-#include "subprocess/ide-subprocess.h"
-#include "threading/ide-task.h"
+#include "ide-build-pipeline.h"
+#include "ide-build-stage.h"
+#include "ide-build-stage-private.h"
 
 typedef struct
 {
@@ -488,6 +485,19 @@ ide_build_stage_class_init (IdeBuildStageClass *klass)
 
   g_object_class_install_properties (object_class, N_PROPS, properties);
 
+  /**
+   * IdeBuildStage:chain:
+   *
+   * We might want to be able to "chain" multiple stages into a single stage
+   * so that we can avoid duplicate work. For example, if we have a "make"
+   * stage immediately follwed by a "make install" stage, it does not make
+   * sense to perform them both individually.
+   *
+   * Returns: %TRUE if @next's work was chained into @self for the next
+   *    execution of the pipeline.
+   *
+   * Since: 3.32
+   */
   signals [CHAIN] =
     g_signal_new ("chain",
                   G_TYPE_FROM_CLASS (klass),
@@ -498,6 +508,32 @@ ide_build_stage_class_init (IdeBuildStageClass *klass)
                   NULL,
                   G_TYPE_BOOLEAN, 1, IDE_TYPE_BUILD_STAGE);
 
+  /**
+   * IdeBuildStage::query:
+   * @self: An #IdeBuildStage
+   * @pipeline: An #IdeBuildPipeline
+   * @targets: (element-type IdeBuildTarget) (nullable): an array
+   *   of #IdeBuildTarget or %NULL
+   * @cancellable: (nullable): a #GCancellable or %NULL
+   *
+   * The #IdeBuildStage::query signal is emitted to request that the
+   * build stage update its completed stage from any external resources.
+   *
+   * This can be useful if you want to use an existing build stage instances
+   * and use a signal to pause forward progress until an external system
+   * has been checked.
+   *
+   * The targets that the user would like to ensure are built are provided
+   * as @targets. Some #IdeBuildStage may use this to reduce the amount
+   * of work they perform
+   *
+   * For example, in a signal handler, you may call ide_build_stage_pause()
+   * and perform an external operation. Forward progress of the stage will
+   * be paused until a matching number of ide_build_stage_unpause() calls
+   * have been made.
+   *
+   * Since: 3.32
+   */
   signals [QUERY] =
     g_signal_new ("query",
                   G_TYPE_FROM_CLASS (klass),
@@ -505,10 +541,24 @@ ide_build_stage_class_init (IdeBuildStageClass *klass)
                   G_STRUCT_OFFSET (IdeBuildStageClass, query),
                   NULL, NULL, NULL,
                   G_TYPE_NONE,
-                  2,
+                  3,
                   IDE_TYPE_BUILD_PIPELINE,
+                  G_TYPE_PTR_ARRAY,
                   G_TYPE_CANCELLABLE);
 
+  /**
+   * IdeBuildStage::reap:
+   * @self: An #IdeBuildStage
+   * @reaper: An #DzlDirectoryReaper
+   *
+   * This signal is emitted when a request to rebuild the project has
+   * occurred. This allows build stages to ensure that certain files are
+   * removed from the system. For example, an autotools build stage might
+   * request that "configure" is removed so that autogen.sh will be executed
+   * as part of the next build.
+   *
+   * Since: 3.32
+   */
   signals [REAP] =
     g_signal_new ("reap",
                   G_TYPE_FROM_CLASS (klass),
@@ -906,11 +956,13 @@ ide_build_stage_unpause (IdeBuildStage *self)
 void
 _ide_build_stage_execute_with_query_async (IdeBuildStage       *self,
                                            IdeBuildPipeline    *pipeline,
+                                           GPtrArray           *targets,
                                            GCancellable        *cancellable,
                                            GAsyncReadyCallback  callback,
                                            gpointer             user_data)
 {
   IdeBuildStagePrivate *priv = ide_build_stage_get_instance_private (self);
+  g_autoptr(GPtrArray) local_targets = NULL;
   g_autoptr(IdeTask) task = NULL;
 
   g_return_if_fail (IDE_IS_BUILD_STAGE (self));
@@ -921,6 +973,9 @@ _ide_build_stage_execute_with_query_async (IdeBuildStage       *self,
   ide_task_set_source_tag (task, _ide_build_stage_execute_with_query_async);
   ide_task_set_task_data (task, g_object_ref (pipeline), g_object_unref);
 
+  if (targets == NULL)
+    targets = local_targets = g_ptr_array_new_with_free_func (g_object_unref);
+
   if (priv->queued_execute != NULL)
     {
       ide_task_return_new_error (task,
@@ -938,7 +993,7 @@ _ide_build_stage_execute_with_query_async (IdeBuildStage       *self,
    * us to share the code-path to make progress on the build stage.
    */
   ide_build_stage_pause (self);
-  g_signal_emit (self, signals [QUERY], 0, pipeline, cancellable);
+  g_signal_emit (self, signals [QUERY], 0, pipeline, targets, cancellable);
   ide_build_stage_unpause (self);
 }
 
diff --git a/src/libide/buildsystem/ide-build-stage.h b/src/libide/foundry/ide-build-stage.h
similarity index 81%
rename from src/libide/buildsystem/ide-build-stage.h
rename to src/libide/foundry/ide-build-stage.h
index bef596314..1dbb2818c 100644
--- a/src/libide/buildsystem/ide-build-stage.h
+++ b/src/libide/foundry/ide-build-stage.h
@@ -20,15 +20,15 @@
 
 #pragma once
 
-#include <dazzle.h>
-#include <gio/gio.h>
-
-#include "ide-version-macros.h"
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
 
-#include "ide-types.h"
-#include "ide-object.h"
+#include <dazzle.h>
+#include <libide-core.h>
 
-#include "buildsystem/ide-build-log.h"
+#include "ide-build-log.h"
+#include "ide-foundry-types.h"
 
 G_BEGIN_DECLS
 
@@ -49,6 +49,8 @@ struct _IdeBuildStageClass
    * vfuncs.
    *
    * Only use thread-safe API from this function.
+   *
+   * Since: 3.32
    */
   gboolean (*execute)        (IdeBuildStage        *self,
                               IdeBuildPipeline     *pipeline,
@@ -60,6 +62,8 @@ struct _IdeBuildStageClass
    *
    * Asynchronous version of the #IdeBuildStage API. This is the preferred
    * way to subclass #IdeBuildStage.
+   *
+   * Since: 3.32
    */
   void     (*execute_async)  (IdeBuildStage        *self,
                               IdeBuildPipeline     *pipeline,
@@ -74,6 +78,8 @@ struct _IdeBuildStageClass
    *
    * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
    *   Upon failure, the pipeline will be stopped.
+   *
+   * Since: 3.32
    */
   gboolean (*execute_finish) (IdeBuildStage        *self,
                               GAsyncResult         *result,
@@ -88,6 +94,8 @@ struct _IdeBuildStageClass
    * @user_data: user data for @callback
    *
    * This function will perform the clean operation.
+   *
+   * Since: 3.32
    */
   void     (*clean_async)    (IdeBuildStage        *self,
                               IdeBuildPipeline     *pipeline,
@@ -104,75 +112,25 @@ struct _IdeBuildStageClass
    * Completes an async operation to ide_build_stage_clean_async().
    *
    * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
+   *
+   * Since: 3.32
    */
   gboolean (*clean_finish)   (IdeBuildStage        *self,
                               GAsyncResult         *result,
                               GError              **error);
 
-  /**
-   * IdeBuildStage::query:
-   * @self: An #IdeBuildStage
-   * @pipeline: An #IdeBuildPipeline
-   * @cancellable: (nullable): a #GCancellable or %NULL
-   *
-   * The #IdeBuildStage::query signal is emitted to request that the
-   * build stage update its completed stage from any external resources.
-   *
-   * This can be useful if you want to use an existing build stage instances
-   * and use a signal to pause forward progress until an external system
-   * has been checked.
-   *
-   * For example, in a signal handler, you may call ide_build_stage_pause()
-   * and perform an external operation. Forward progress of the stage will
-   * be paused until a matching number of ide_build_stage_unpause() calls
-   * have been made.
-   */
+  /* Signals */
   void     (*query)          (IdeBuildStage        *self,
                               IdeBuildPipeline     *pipeline,
+                              GPtrArray            *targets,
                               GCancellable         *cancellable);
-
-  /**
-   * IdeBuildStage::reap:
-   * @self: An #IdeBuildStage
-   * @reaper: An #DzlDirectoryReaper
-   *
-   * This signal is emitted when a request to rebuild the project has
-   * occurred. This allows build stages to ensure that certain files are
-   * removed from the system. For example, an autotools build stage might
-   * request that "configure" is removed so that autogen.sh will be executed
-   * as part of the next build.
-   */
   void     (*reap)           (IdeBuildStage        *self,
                               DzlDirectoryReaper   *reaper);
-
-
-  /**
-   * IdeBuildStage:chain:
-   *
-   * We might want to be able to "chain" multiple stages into a single stage
-   * so that we can avoid duplicate work. For example, if we have a "make"
-   * stage immediately follwed by a "make install" stage, it does not make
-   * sense to perform them both individually.
-   *
-   * Returns: %TRUE if @next's work was chained into @self for the next
-   *    execution of the pipeline.
-   */
   gboolean (*chain)          (IdeBuildStage        *self,
                               IdeBuildStage        *next);
 
   /*< private >*/
-  gpointer _reserved1;
-  gpointer _reserved2;
-  gpointer _reserved3;
-  gpointer _reserved4;
-  gpointer _reserved5;
-  gpointer _reserved6;
-  gpointer _reserved7;
-  gpointer _reserved8;
-  gpointer _reserved9;
-  gpointer _reserved10;
-  gpointer _reserved11;
-  gpointer _reserved12;
+  gpointer _reserved[16];
 };
 
 IDE_AVAILABLE_IN_3_32
diff --git a/src/libide/buildsystem/ide-build-system-discovery.c 
b/src/libide/foundry/ide-build-system-discovery.c
similarity index 98%
rename from src/libide/buildsystem/ide-build-system-discovery.c
rename to src/libide/foundry/ide-build-system-discovery.c
index c15720c8e..63477135e 100644
--- a/src/libide/buildsystem/ide-build-system-discovery.c
+++ b/src/libide/foundry/ide-build-system-discovery.c
@@ -22,7 +22,7 @@
 
 #include "config.h"
 
-#include "buildsystem/ide-build-system-discovery.h"
+#include "ide-build-system-discovery.h"
 
 G_DEFINE_INTERFACE (IdeBuildSystemDiscovery, ide_build_system_discovery, G_TYPE_OBJECT)
 
diff --git a/src/libide/buildsystem/ide-build-system-discovery.h 
b/src/libide/foundry/ide-build-system-discovery.h
similarity index 90%
rename from src/libide/buildsystem/ide-build-system-discovery.h
rename to src/libide/foundry/ide-build-system-discovery.h
index 966a22093..6d9a8f201 100644
--- a/src/libide/buildsystem/ide-build-system-discovery.h
+++ b/src/libide/foundry/ide-build-system-discovery.h
@@ -20,11 +20,13 @@
 
 #pragma once
 
-#include <gio/gio.h>
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <libide-core.h>
 
-#include "ide-types.h"
+#include "ide-foundry-types.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/buildsystem/ide-build-system.c b/src/libide/foundry/ide-build-system.c
similarity index 81%
rename from src/libide/buildsystem/ide-build-system.c
rename to src/libide/foundry/ide-build-system.c
index 1fb5f0273..d2820d7fd 100644
--- a/src/libide/buildsystem/ide-build-system.c
+++ b/src/libide/foundry/ide-build-system.c
@@ -22,22 +22,19 @@
 
 #include "config.h"
 
+#include <libide-code.h>
+#include <libide-projects.h>
+#include <libide-threading.h>
+#include <libide-vcs.h>
 #include <string.h>
 
-#include "ide-context.h"
-#include "ide-debug.h"
-#include "ide-object.h"
-
-#include "buildsystem/ide-build-manager.h"
-#include "buildsystem/ide-build-pipeline.h"
-#include "buildsystem/ide-build-system.h"
-#include "config/ide-configuration.h"
-#include "devices/ide-device.h"
-#include "files/ide-file.h"
-#include "projects/ide-project.h"
-#include "util/ide-glib.h"
-#include "vcs/ide-vcs.h"
-#include "threading/ide-task.h"
+#include "ide-build-manager.h"
+#include "ide-build-pipeline.h"
+#include "ide-build-system.h"
+#include "ide-configuration.h"
+#include "ide-device.h"
+#include "ide-foundry-compat.h"
+#include "ide-toolchain.h"
 
 G_DEFINE_INTERFACE (IdeBuildSystem, ide_build_system, IDE_TYPE_OBJECT)
 
@@ -81,7 +78,7 @@ ide_build_system_get_priority (IdeBuildSystem *self)
 
 static void
 ide_build_system_real_get_build_flags_async (IdeBuildSystem      *self,
-                                             IdeFile             *file,
+                                             GFile               *file,
                                              GCancellable        *cancellable,
                                              GAsyncReadyCallback  callback,
                                              gpointer             user_data)
@@ -113,7 +110,7 @@ ide_build_system_get_build_flags_cb (GObject      *object,
   g_autoptr(GError) error = NULL;
   g_auto(GStrv) flags = NULL;
   GetBuildFlagsData *data;
-  IdeFile *file;
+  GFile *file;
 
   g_assert (IDE_IS_BUILD_SYSTEM (self));
   g_assert (G_IS_ASYNC_RESULT (result));
@@ -127,7 +124,7 @@ ide_build_system_get_build_flags_cb (GObject      *object,
   g_assert (data->flags != NULL);
 
   file = g_ptr_array_index (data->files, data->index);
-  g_assert (IDE_IS_FILE (file));
+  g_assert (G_IS_FILE (file));
 
   data->index++;
 
@@ -135,7 +132,7 @@ ide_build_system_get_build_flags_cb (GObject      *object,
 
   if (error != NULL)
     g_debug ("Failed to load build flags for \"%s\": %s",
-             ide_file_get_path (file),
+             g_file_peek_path (file),
              error->message);
   else
     g_hash_table_insert (data->flags, g_object_ref (file), g_steal_pointer (&flags));
@@ -148,7 +145,7 @@ ide_build_system_get_build_flags_cb (GObject      *object,
       GCancellable *cancellable = ide_task_get_cancellable (task);
 
       file = g_ptr_array_index (data->files, data->index);
-      g_assert (IDE_IS_FILE (file));
+      g_assert (G_IS_FILE (file));
 
       ide_build_system_get_build_flags_async (self,
                                               file,
@@ -163,6 +160,15 @@ ide_build_system_get_build_flags_cb (GObject      *object,
                            (GDestroyNotify)g_hash_table_unref);
 }
 
+static GPtrArray *
+copy_files (GPtrArray *in)
+{
+  GPtrArray *out = g_ptr_array_new_full (in->len, g_object_unref);
+  for (guint i = 0; i < in->len; i++)
+    g_ptr_array_add (out, g_file_dup (g_ptr_array_index (in, i)));
+  return g_steal_pointer (&out);
+}
+
 static void
 ide_build_system_real_get_build_flags_for_files_async (IdeBuildSystem       *self,
                                                        GPtrArray            *files,
@@ -191,18 +197,17 @@ ide_build_system_real_get_build_flags_for_files_async (IdeBuildSystem       *sel
     }
 
   g_assert (files->len > 0);
-  g_assert (IDE_IS_FILE (g_ptr_array_index (files, 0)));
+  g_assert (G_IS_FILE (g_ptr_array_index (files, 0)));
 
   if (ide_task_return_error_if_cancelled (task))
     return;
 
   data = g_slice_new0 (GetBuildFlagsData);
-  data->files = g_ptr_array_ref (files);
-  data->flags = g_hash_table_new_full ((GHashFunc)ide_file_hash,
-                                       (GEqualFunc)ide_file_equal,
+  data->files = copy_files (files);
+  data->flags = g_hash_table_new_full ((GHashFunc)g_file_hash,
+                                       (GEqualFunc)g_file_equal,
                                        g_object_unref,
                                        (GDestroyNotify)g_strfreev);
-
   ide_task_set_task_data (task, data, get_build_flags_data_free);
 
   ide_build_system_get_build_flags_async (self,
@@ -234,104 +239,8 @@ ide_build_system_default_init (IdeBuildSystemInterface *iface)
                          "The project file.",
                          G_TYPE_FILE,
                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-  g_object_interface_install_property (iface, properties [PROP_PROJECT_FILE]);
-}
-
-static gint
-sort_priority (gconstpointer a,
-               gconstpointer b,
-               gpointer      data)
-{
-  IdeBuildSystem **as = (IdeBuildSystem **)a;
-  IdeBuildSystem **bs = (IdeBuildSystem **)b;
-  g_autofree gchar *id_a = ide_build_system_get_id (*as);
-  g_autofree gchar *id_b = ide_build_system_get_id (*bs);
-  const gchar *build_system_hint = data;
-
-  if (build_system_hint != NULL)
-    {
-      if (g_strcmp0 (build_system_hint, id_a) == 0)
-        return -1;
-      else if (g_strcmp0 (build_system_hint, id_b) == 0)
-        return 1;
-    }
 
-  return ide_build_system_get_priority (*as) - ide_build_system_get_priority (*bs);
-}
-
-/**
- * ide_build_system_new_async:
- * @context: #IdeBuildSystem
- * @project_file: a #GFile containing the directory or project file.
- * @build_system_hint: A hint for the build system to use
- * @cancellable: (allow-none): a #GCancellable
- * @callback: A callback to execute upon completion
- * @user_data: User data for @callback.
- *
- * Asynchronously creates a new #IdeBuildSystem instance using the registered
- * #GIOExtensionPoint system. Each extension point will be tried asynchronously
- * by priority until one has been found that supports @project_file.
- *
- * If no build system could be found, then ide_build_system_new_finish() will
- * return %NULL.
- *
- * Since: 3.32
- */
-void
-ide_build_system_new_async (IdeContext          *context,
-                            GFile               *project_file,
-                            const gchar         *build_system_hint,
-                            GCancellable        *cancellable,
-                            GAsyncReadyCallback  callback,
-                            gpointer             user_data)
-{
-  g_return_if_fail (IDE_IS_CONTEXT (context));
-  g_return_if_fail (G_IS_FILE (project_file));
-  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  IDE_TRACE_MSG ("Creating build system with hint \"%s\"", build_system_hint ?: "");
-
-  ide_object_new_for_extension_async (IDE_TYPE_BUILD_SYSTEM,
-                                      sort_priority, (gpointer)build_system_hint,
-                                      G_PRIORITY_LOW,
-                                      cancellable,
-                                      callback,
-                                      user_data,
-                                      "context", context,
-                                      "project-file", project_file,
-                                      NULL);
-}
-
-/**
- * ide_build_system_new_finish:
- *
- * Complete an asynchronous call to ide_build_system_new_async().
- *
- * Returns: (transfer full): An #IdeBuildSystem if successful; otherwise
- *   %NULL and @error is set.
- *
- * Since: 3.32
- */
-IdeBuildSystem *
-ide_build_system_new_finish (GAsyncResult  *result,
-                             GError       **error)
-{
-  IdeObject *ret;
-
-  IDE_ENTRY;
-
-  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
-
-  ret = ide_object_new_finish (result, error);
-
-#ifdef IDE_ENABLE_TRACE
-  if (ret)
-    IDE_TRACE_MSG ("BuildSystem is %s", G_OBJECT_TYPE_NAME (ret));
-  else
-    IDE_TRACE_MSG ("BuildSystem creation failed");
-#endif
-
-  IDE_RETURN (IDE_BUILD_SYSTEM (ret));
+  g_object_interface_install_property (iface, properties [PROP_PROJECT_FILE]);
 }
 
 static gchar *
@@ -379,7 +288,7 @@ ide_build_system_post_process_build_flags (IdeBuildSystem  *self,
     return;
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  build_manager = ide_context_get_build_manager (context);
+  build_manager = ide_build_manager_from_context (context);
   pipeline = ide_build_manager_get_pipeline (build_manager);
 
   for (guint i = 0; flags[i] != NULL; i++)
@@ -427,7 +336,7 @@ ide_build_system_post_process_build_flags (IdeBuildSystem  *self,
 
 void
 ide_build_system_get_build_flags_async (IdeBuildSystem      *self,
-                                        IdeFile             *file,
+                                        GFile               *file,
                                         GCancellable        *cancellable,
                                         GAsyncReadyCallback  callback,
                                         gpointer             user_data)
@@ -435,7 +344,7 @@ ide_build_system_get_build_flags_async (IdeBuildSystem      *self,
   IDE_ENTRY;
 
   g_return_if_fail (IDE_IS_BUILD_SYSTEM (self));
-  g_return_if_fail (IDE_IS_FILE (file));
+  g_return_if_fail (G_IS_FILE (file));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_flags_async (self, file, cancellable, callback, user_data);
@@ -472,7 +381,7 @@ ide_build_system_get_build_flags_finish (IdeBuildSystem  *self,
 /**
  * ide_build_system_get_build_flags_for_files_async:
  * @self: An #IdeBuildSystem instance.
- * @files: (element-type Ide.File): array of files whose build flags has to be retrieved.
+ * @files: (element-type GFile) (transfer none): array of files whose build flags has to be retrieved.
  * @cancellable: (allow-none): a #GCancellable to cancel getting build flags.
  * @callback: function to be called after getting build flags.
  * @user_data: data to pass to @callback.
@@ -495,8 +404,7 @@ ide_build_system_get_build_flags_for_files_async (IdeBuildSystem       *self,
   g_return_if_fail ( files != NULL);
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  IDE_BUILD_SYSTEM_GET_IFACE (self)->
-      get_build_flags_for_files_async (self, files, cancellable, callback, user_data);
+  IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_flags_for_files_async (self, files, cancellable, callback, 
user_data);
 
   IDE_EXIT;
 }
@@ -507,7 +415,7 @@ ide_build_system_get_build_flags_for_files_async (IdeBuildSystem       *self,
  * @result: a #GAsyncResult
  * @error: a location for a #GError or %NULL
  *
- * Returns: (element-type Ide.File GStrv) (transfer full): a #GHashTable or #IdeFile to #GStrv
+ * Returns: (element-type Ide.File GStrv) (transfer full): a #GHashTable or #GFile to #GStrv
  *
  * Since: 3.32
  */
@@ -565,7 +473,7 @@ ide_build_system_get_builddir (IdeBuildSystem   *self,
       IdeVcs *vcs;
 
       context = ide_object_get_context (IDE_OBJECT (self));
-      vcs = ide_context_get_vcs (context);
+      vcs = ide_vcs_from_context (context);
       config = ide_build_pipeline_get_configuration (pipeline);
       config_id = ide_configuration_get_id (config);
       runtime = ide_build_pipeline_get_runtime (pipeline);
@@ -661,7 +569,7 @@ ide_build_system_get_build_flags_for_dir_cb (GObject      *object,
 
   self = ide_task_get_source_object (task);
   context = ide_object_get_context (IDE_OBJECT (self));
-  vcs = ide_context_get_vcs (context);
+  vcs = ide_vcs_from_context (context);
   cancellable = ide_task_get_cancellable (task);
   files = g_ptr_array_new_with_free_func (g_object_unref);
 
@@ -676,7 +584,7 @@ ide_build_system_get_build_flags_for_dir_cb (GObject      *object,
           g_autoptr(GFile) child = g_file_get_child (dir, name);
 
           if (!ide_vcs_is_ignored (vcs, child, NULL))
-            g_ptr_array_add (files, ide_file_new (context, child));
+            g_ptr_array_add (files, g_steal_pointer (&child));
         }
     }
 
@@ -720,7 +628,7 @@ ide_build_system_get_build_flags_for_dir_async (IdeBuildSystem      *self,
  * @result: a #GAsyncResult
  * @error: a location for a #GError or %NULL
  *
- * Returns: (element-type Ide.File GStrv) (transfer full): a #GHashTable of #IdeFile to #GStrv
+ * Returns: (element-type Ide.File GStrv) (transfer full): a #GHashTable of #GFile to #GStrv
  *
  * Since: 3.32
  */
diff --git a/src/libide/buildsystem/ide-build-system.h b/src/libide/foundry/ide-build-system.h
similarity index 86%
rename from src/libide/buildsystem/ide-build-system.h
rename to src/libide/foundry/ide-build-system.h
index ebf69c616..fe2b5c6a1 100644
--- a/src/libide/buildsystem/ide-build-system.h
+++ b/src/libide/foundry/ide-build-system.h
@@ -20,11 +20,14 @@
 
 #pragma once
 
-#include <gio/gio.h>
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <libide-core.h>
+#include <libide-code.h>
 
-#include "ide-object.h"
+#include "ide-foundry-types.h"
 
 G_BEGIN_DECLS
 
@@ -39,7 +42,7 @@ struct _IdeBuildSystemInterface
 
   gint        (*get_priority)                      (IdeBuildSystem       *self);
   void        (*get_build_flags_async)             (IdeBuildSystem       *self,
-                                                    IdeFile              *file,
+                                                    GFile                *file,
                                                     GCancellable         *cancellable,
                                                     GAsyncReadyCallback   callback,
                                                     gpointer              user_data);
@@ -62,28 +65,20 @@ struct _IdeBuildSystemInterface
                                                     IdeToolchain         *toolchain);
 };
 
+IDE_AVAILABLE_IN_3_32
+IdeBuildSystem *ide_build_system_from_context                      (IdeContext           *context);
 IDE_AVAILABLE_IN_3_32
 gchar          *ide_build_system_get_id                            (IdeBuildSystem       *self);
 IDE_AVAILABLE_IN_3_32
 gchar          *ide_build_system_get_display_name                  (IdeBuildSystem       *self);
 IDE_AVAILABLE_IN_3_32
-void            ide_build_system_new_async                         (IdeContext           *context,
-                                                                    GFile                *project_file,
-                                                                    const gchar          *build_system_hint,
-                                                                    GCancellable         *cancellable,
-                                                                    GAsyncReadyCallback   callback,
-                                                                    gpointer              user_data);
-IDE_AVAILABLE_IN_3_32
-IdeBuildSystem *ide_build_system_new_finish                        (GAsyncResult         *result,
-                                                                    GError              **error);
-IDE_AVAILABLE_IN_3_32
 gint            ide_build_system_get_priority                      (IdeBuildSystem       *self);
 IDE_AVAILABLE_IN_3_32
 gchar          *ide_build_system_get_builddir                      (IdeBuildSystem       *self,
                                                                     IdeBuildPipeline     *pipeline);
 IDE_AVAILABLE_IN_3_32
 void            ide_build_system_get_build_flags_async             (IdeBuildSystem       *self,
-                                                                    IdeFile              *file,
+                                                                    GFile                *file,
                                                                     GCancellable         *cancellable,
                                                                     GAsyncReadyCallback   callback,
                                                                     gpointer              user_data);
diff --git a/src/libide/buildsystem/ide-build-target-provider.c 
b/src/libide/foundry/ide-build-target-provider.c
similarity index 91%
rename from src/libide/buildsystem/ide-build-target-provider.c
rename to src/libide/foundry/ide-build-target-provider.c
index 47334d208..d351bee05 100644
--- a/src/libide/buildsystem/ide-build-target-provider.c
+++ b/src/libide/foundry/ide-build-target-provider.c
@@ -22,10 +22,7 @@
 
 #include "config.h"
 
-#include "ide-context.h"
-#include "ide-debug.h"
-
-#include "buildsystem/ide-build-target-provider.h"
+#include "ide-build-target-provider.h"
 
 G_DEFINE_INTERFACE (IdeBuildTargetProvider, ide_build_target_provider, G_TYPE_OBJECT)
 
@@ -56,11 +53,6 @@ ide_build_target_provider_default_init (IdeBuildTargetProviderInterface *iface)
 {
   iface->get_targets_async = ide_build_target_provider_real_get_targets_async;
   iface->get_targets_finish = ide_build_target_provider_real_get_targets_finish;
-
-  g_object_interface_install_property (iface,
-                                       g_param_spec_object ("context", NULL, NULL,
-                                                            IDE_TYPE_CONTEXT,
-                                                            (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 
G_PARAM_STATIC_STRINGS)));
 }
 
 /**
diff --git a/src/libide/buildsystem/ide-build-target-provider.h 
b/src/libide/foundry/ide-build-target-provider.h
similarity index 93%
rename from src/libide/buildsystem/ide-build-target-provider.h
rename to src/libide/foundry/ide-build-target-provider.h
index 0ddec95d3..067347da7 100644
--- a/src/libide/buildsystem/ide-build-target-provider.h
+++ b/src/libide/foundry/ide-build-target-provider.h
@@ -20,8 +20,11 @@
 
 #pragma once
 
-#include "ide-object.h"
-#include "ide-version-macros.h"
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/buildsystem/ide-build-target.c b/src/libide/foundry/ide-build-target.c
similarity index 86%
rename from src/libide/buildsystem/ide-build-target.c
rename to src/libide/foundry/ide-build-target.c
index f3b073aeb..291597716 100644
--- a/src/libide/buildsystem/ide-build-target.c
+++ b/src/libide/foundry/ide-build-target.c
@@ -22,7 +22,7 @@
 
 #include "config.h"
 
-#include "buildsystem/ide-build-target.h"
+#include "ide-build-target.h"
 
 G_DEFINE_INTERFACE (IdeBuildTarget, ide_build_target, IDE_TYPE_OBJECT)
 
@@ -64,6 +64,29 @@ ide_build_target_get_install_directory (IdeBuildTarget *self)
   return NULL;
 }
 
+/**
+ * ide_build_target_get_install:
+ * @self: an #IdeBuildTarget
+ *
+ * Checks if the #IdeBuildTarget gets installed.
+ *
+ * Returns: %TRUE if the build target is installed
+ *
+ * Since: 3.32
+ */
+gboolean
+ide_build_target_get_install (IdeBuildTarget *self)
+{
+  g_autoptr(GFile) dir = NULL;
+
+  g_return_val_if_fail (IDE_IS_BUILD_TARGET (self), FALSE);
+
+  if ((dir = ide_build_target_get_install_directory (self)))
+    return TRUE;
+
+  return FALSE;
+}
+
 /**
  * ide_build_target_get_name:
  *
@@ -104,6 +127,27 @@ ide_build_target_get_priority (IdeBuildTarget *self)
   return 0;
 }
 
+/**
+ * ide_build_target_get_kind:
+ * @self: a #IdeBuildTarget
+ *
+ * Gets the kind of artifact.
+ *
+ * Returns: an #IdeArtifactKind
+ *
+ * Since: 3.32
+ */
+IdeArtifactKind
+ide_build_target_get_kind (IdeBuildTarget *self)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_TARGET (self), 0);
+
+  if (IDE_BUILD_TARGET_GET_IFACE (self)->get_kind)
+    return IDE_BUILD_TARGET_GET_IFACE (self)->get_kind (self);
+
+  return IDE_ARTIFACT_KIND_NONE;
+}
+
 gint
 ide_build_target_compare (const IdeBuildTarget *left,
                           const IdeBuildTarget *right)
diff --git a/src/libide/foundry/ide-build-target.h b/src/libide/foundry/ide-build-target.h
new file mode 100644
index 000000000..8da2fdb68
--- /dev/null
+++ b/src/libide/foundry/ide-build-target.h
@@ -0,0 +1,80 @@
+/* ide-build-target.h
+ *
+ * Copyright 2016-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-foundry-types.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BUILD_TARGET (ide_build_target_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_INTERFACE (IdeBuildTarget, ide_build_target, IDE, BUILD_TARGET, IdeObject)
+
+typedef enum
+{
+  IDE_ARTIFACT_KIND_NONE,
+  IDE_ARTIFACT_KIND_EXECUTABLE,
+  IDE_ARTIFACT_KIND_SHARED_LIBRARY,
+  IDE_ARTIFACT_KIND_STATIC_LIBRARY,
+  IDE_ARTIFACT_KIND_FILE,
+} IdeArtifactKind;
+
+struct _IdeBuildTargetInterface
+{
+  GTypeInterface parent_iface;
+
+  GFile            *(*get_install_directory) (IdeBuildTarget *self);
+  gchar            *(*get_name)              (IdeBuildTarget *self);
+  gint              (*get_priority)          (IdeBuildTarget *self);
+  gchar           **(*get_argv)              (IdeBuildTarget *self);
+  gchar            *(*get_cwd)               (IdeBuildTarget *self);
+  gchar            *(*get_language)          (IdeBuildTarget *self);
+  IdeArtifactKind   (*get_kind)              (IdeBuildTarget *self);
+};
+
+IDE_AVAILABLE_IN_3_32
+GFile            *ide_build_target_get_install_directory (IdeBuildTarget       *self);
+IDE_AVAILABLE_IN_3_32
+gchar            *ide_build_target_get_name              (IdeBuildTarget       *self);
+IDE_AVAILABLE_IN_3_32
+gint              ide_build_target_get_priority          (IdeBuildTarget       *self);
+IDE_AVAILABLE_IN_3_32
+gchar           **ide_build_target_get_argv              (IdeBuildTarget       *self);
+IDE_AVAILABLE_IN_3_32
+gchar            *ide_build_target_get_cwd               (IdeBuildTarget       *self);
+IDE_AVAILABLE_IN_3_32
+gchar            *ide_build_target_get_language          (IdeBuildTarget       *self);
+IDE_AVAILABLE_IN_3_32
+gboolean          ide_build_target_get_install           (IdeBuildTarget       *self);
+IDE_AVAILABLE_IN_3_32
+IdeArtifactKind   ide_build_target_get_kind              (IdeBuildTarget       *self);
+IDE_AVAILABLE_IN_3_32
+gboolean          ide_build_target_compare               (const IdeBuildTarget *left,
+                                                          const IdeBuildTarget *right);
+
+G_END_DECLS
diff --git a/src/libide/buildsystem/ide-build-utils.c b/src/libide/foundry/ide-build-utils.c
similarity index 86%
rename from src/libide/buildsystem/ide-build-utils.c
rename to src/libide/foundry/ide-build-utils.c
index f8839a3db..18683cede 100644
--- a/src/libide/buildsystem/ide-build-utils.c
+++ b/src/libide/foundry/ide-build-utils.c
@@ -18,12 +18,16 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
-#include "buildsystem/ide-build-utils.h"
+#define G_LOG_DOMAIN "ide-build-utils"
+
+#include "config.h"
+
+#include "ide-build-private.h"
 
 guint8 *
-ide_build_utils_filter_color_codes (const guint8 *data,
-                                    gsize         len,
-                                    gsize        *out_len)
+_ide_build_utils_filter_color_codes (const guint8 *data,
+                                     gsize         len,
+                                     gsize        *out_len)
 {
   g_autoptr(GByteArray) dst = NULL;
 
diff --git a/src/libide/buildsystem/ide-compile-commands.c b/src/libide/foundry/ide-compile-commands.c
similarity index 98%
rename from src/libide/buildsystem/ide-compile-commands.c
rename to src/libide/foundry/ide-compile-commands.c
index a43eb09f0..76a4dd3cb 100644
--- a/src/libide/buildsystem/ide-compile-commands.c
+++ b/src/libide/foundry/ide-compile-commands.c
@@ -24,12 +24,10 @@
 
 #include <dazzle.h>
 #include <json-glib/json-glib.h>
+#include <libide-threading.h>
 #include <string.h>
 
-#include "ide-debug.h"
-
-#include "buildsystem/ide-compile-commands.h"
-#include "threading/ide-task.h"
+#include "ide-compile-commands.h"
 
 /**
  * SECTION:ide-compile-commands
@@ -514,12 +512,12 @@ ide_compile_commands_filter_c (IdeCompileCommands   *self,
 
         default:
           if (g_str_has_prefix (param, "-std=") ||
-              dzl_str_equal0 (param, "-pthread") ||
+              ide_str_equal0 (param, "-pthread") ||
               g_str_has_prefix (param, "-isystem"))
             {
               g_ptr_array_add (ar, g_strdup (param));
             }
-          else if (next != NULL && dzl_str_equal0 (param, "-include"))
+          else if (next != NULL && ide_str_equal0 (param, "-include"))
             {
               g_ptr_array_add (ar, g_strdup (param));
               g_ptr_array_add (ar, ide_compile_commands_resolve (self, info, next));
@@ -710,7 +708,7 @@ ide_compile_commands_lookup (IdeCompileCommands   *self,
    * document we stored information about each of the Vala files in a special
    * list for exactly this purpose.
    */
-  if (dzl_str_equal0 (dot, ".vala") && self->vala_info != NULL)
+  if (ide_str_equal0 (dot, ".vala") && self->vala_info != NULL)
     {
       for (guint i = 0; i < self->vala_info->len; i++)
         {
diff --git a/src/libide/buildsystem/ide-compile-commands.h b/src/libide/foundry/ide-compile-commands.h
similarity index 94%
rename from src/libide/buildsystem/ide-compile-commands.h
rename to src/libide/foundry/ide-compile-commands.h
index 7febca6e0..8c88171a0 100644
--- a/src/libide/buildsystem/ide-compile-commands.h
+++ b/src/libide/foundry/ide-compile-commands.h
@@ -20,9 +20,11 @@
 
 #pragma once
 
-#include <gio/gio.h>
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/buildsystem/ide-dependency-updater.c b/src/libide/foundry/ide-dependency-updater.c
similarity index 100%
rename from src/libide/buildsystem/ide-dependency-updater.c
rename to src/libide/foundry/ide-dependency-updater.c
diff --git a/src/libide/buildsystem/ide-dependency-updater.h b/src/libide/foundry/ide-dependency-updater.h
similarity index 92%
rename from src/libide/buildsystem/ide-dependency-updater.h
rename to src/libide/foundry/ide-dependency-updater.h
index e31159892..e585c8f1d 100644
--- a/src/libide/buildsystem/ide-dependency-updater.h
+++ b/src/libide/foundry/ide-dependency-updater.h
@@ -20,8 +20,11 @@
 
 #pragma once
 
-#include "ide-object.h"
-#include "ide-version-macros.h"
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/foundry/ide-simple-build-system-discovery.c 
b/src/libide/foundry/ide-simple-build-system-discovery.c
new file mode 100644
index 000000000..36ed5e74e
--- /dev/null
+++ b/src/libide/foundry/ide-simple-build-system-discovery.c
@@ -0,0 +1,374 @@
+/* ide-simple-build-system-discovery.c
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-simple-build-system-discovery"
+
+#include "config.h"
+
+#include <fnmatch.h>
+
+#include "ide-simple-build-system-discovery.h"
+
+typedef struct
+{
+  gchar *glob;
+  gchar *hint;
+  guint  is_exact : 1;
+  gint   priority;
+} IdeSimpleBuildSystemDiscoveryPrivate;
+
+enum {
+  PROP_0,
+  PROP_GLOB,
+  PROP_HINT,
+  PROP_PRIORITY,
+  N_PROPS
+};
+
+static gchar *
+ide_simple_build_system_discovery_discover (IdeBuildSystemDiscovery  *discovery,
+                                            GFile                    *file,
+                                            GCancellable             *cancellable,
+                                            gint                     *priority,
+                                            GError                  **error);
+
+static void
+build_system_discovery_iface_init (IdeBuildSystemDiscoveryInterface *iface)
+{
+  iface->discover = ide_simple_build_system_discovery_discover;
+}
+
+G_DEFINE_TYPE_WITH_CODE (IdeSimpleBuildSystemDiscovery, ide_simple_build_system_discovery, IDE_TYPE_OBJECT,
+                         G_ADD_PRIVATE (IdeSimpleBuildSystemDiscovery)
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_BUILD_SYSTEM_DISCOVERY,
+                                                build_system_discovery_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_simple_build_system_discovery_finalize (GObject *object)
+{
+  IdeSimpleBuildSystemDiscovery *self = (IdeSimpleBuildSystemDiscovery *)object;
+  IdeSimpleBuildSystemDiscoveryPrivate *priv = ide_simple_build_system_discovery_get_instance_private (self);
+
+  g_clear_pointer (&priv->glob, g_free);
+  g_clear_pointer (&priv->hint, g_free);
+
+  G_OBJECT_CLASS (ide_simple_build_system_discovery_parent_class)->finalize (object);
+}
+
+static void
+ide_simple_build_system_discovery_get_property (GObject    *object,
+                                                guint       prop_id,
+                                                GValue     *value,
+                                                GParamSpec *pspec)
+{
+  IdeSimpleBuildSystemDiscovery *self = IDE_SIMPLE_BUILD_SYSTEM_DISCOVERY (object);
+
+  switch (prop_id)
+    {
+    case PROP_GLOB:
+      g_value_set_string (value, ide_simple_build_system_discovery_get_glob (self));
+      break;
+
+    case PROP_HINT:
+      g_value_set_string (value, ide_simple_build_system_discovery_get_hint (self));
+      break;
+
+    case PROP_PRIORITY:
+      g_value_set_int (value, ide_simple_build_system_discovery_get_priority (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_simple_build_system_discovery_set_property (GObject      *object,
+                                                guint         prop_id,
+                                                const GValue *value,
+                                                GParamSpec   *pspec)
+{
+  IdeSimpleBuildSystemDiscovery *self = IDE_SIMPLE_BUILD_SYSTEM_DISCOVERY (object);
+
+  switch (prop_id)
+    {
+    case PROP_GLOB:
+      ide_simple_build_system_discovery_set_glob (self, g_value_get_string (value));
+      break;
+
+    case PROP_HINT:
+      ide_simple_build_system_discovery_set_hint (self, g_value_get_string (value));
+      break;
+
+    case PROP_PRIORITY:
+      ide_simple_build_system_discovery_set_priority (self, g_value_get_int (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_simple_build_system_discovery_class_init (IdeSimpleBuildSystemDiscoveryClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_simple_build_system_discovery_finalize;
+  object_class->get_property = ide_simple_build_system_discovery_get_property;
+  object_class->set_property = ide_simple_build_system_discovery_set_property;
+
+  /**
+   * IdeSimpleBuildSystemDiscovery:glob:
+   *
+   * The "glob" property is a glob to match for files within the project
+   * directory. This can be used to quickly match the project file, such as
+   * "configure.*".
+   *
+   * Since: 3.32
+   */
+  properties [PROP_GLOB] =
+    g_param_spec_string ("glob",
+                         "Glob",
+                         "The glob to match project filenames",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * IdeSimpleBuildSystemDiscovery:hint:
+   *
+   * The "hint" property is used from ide_build_system_discovery_discover()
+   * if the build file was discovered.
+   *
+   * Since: 3.32
+   */
+  properties [PROP_HINT] =
+    g_param_spec_string ("hint",
+                         "Hint",
+                         "The hint of the plugin supporting the build system",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  /**
+   * IdeSimpleBuildSystemDiscovery:priority:
+   *
+   * The "priority" property is the priority of any match.
+   *
+   * Since: 3.32
+   */
+  properties [PROP_PRIORITY] =
+    g_param_spec_int ("priority",
+                      "Priority",
+                      "The priority of the discovery",
+                      G_MININT, G_MAXINT, 0,
+                      (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_simple_build_system_discovery_init (IdeSimpleBuildSystemDiscovery *self)
+{
+}
+
+const gchar *
+ide_simple_build_system_discovery_get_glob (IdeSimpleBuildSystemDiscovery *self)
+{
+  IdeSimpleBuildSystemDiscoveryPrivate *priv = ide_simple_build_system_discovery_get_instance_private (self);
+  g_return_val_if_fail (IDE_IS_SIMPLE_BUILD_SYSTEM_DISCOVERY (self), NULL);
+  return priv->glob;
+}
+
+const gchar *
+ide_simple_build_system_discovery_get_hint (IdeSimpleBuildSystemDiscovery *self)
+{
+  IdeSimpleBuildSystemDiscoveryPrivate *priv = ide_simple_build_system_discovery_get_instance_private (self);
+  g_return_val_if_fail (IDE_IS_SIMPLE_BUILD_SYSTEM_DISCOVERY (self), NULL);
+  return priv->hint;
+}
+
+gint
+ide_simple_build_system_discovery_get_priority (IdeSimpleBuildSystemDiscovery *self)
+{
+  IdeSimpleBuildSystemDiscoveryPrivate *priv = ide_simple_build_system_discovery_get_instance_private (self);
+  g_return_val_if_fail (IDE_IS_SIMPLE_BUILD_SYSTEM_DISCOVERY (self), 0);
+  return priv->priority;
+}
+
+void
+ide_simple_build_system_discovery_set_glob (IdeSimpleBuildSystemDiscovery *self,
+                                            const gchar                   *glob)
+{
+  IdeSimpleBuildSystemDiscoveryPrivate *priv = ide_simple_build_system_discovery_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_SIMPLE_BUILD_SYSTEM_DISCOVERY (self));
+  g_return_if_fail (glob != NULL);
+
+  if (!ide_str_equal0 (glob, priv->glob))
+    {
+      g_free (priv->glob);
+      priv->glob = g_strdup (glob);
+      priv->is_exact = TRUE;
+      for (; !priv->is_exact && *glob; glob = g_utf8_next_char (glob))
+        {
+          gunichar ch = g_utf8_get_char (glob);
+
+          switch (ch)
+            {
+            case '(': case '!': case '*': case '[': case '{': case '|':
+              priv->is_exact = FALSE;
+              break;
+
+            default:
+              break;
+            }
+        }
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_GLOB]);
+    }
+}
+
+void
+ide_simple_build_system_discovery_set_hint (IdeSimpleBuildSystemDiscovery *self,
+                                            const gchar                   *hint)
+{
+  IdeSimpleBuildSystemDiscoveryPrivate *priv = ide_simple_build_system_discovery_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_SIMPLE_BUILD_SYSTEM_DISCOVERY (self));
+
+  if (!ide_str_equal0 (hint, priv->hint))
+    {
+      g_free (priv->hint);
+      priv->hint = g_strdup (hint);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_HINT]);
+    }
+}
+
+void
+ide_simple_build_system_discovery_set_priority (IdeSimpleBuildSystemDiscovery *self,
+                                                gint                           priority)
+{
+  IdeSimpleBuildSystemDiscoveryPrivate *priv = ide_simple_build_system_discovery_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_SIMPLE_BUILD_SYSTEM_DISCOVERY (self));
+
+  if (priority != priv->priority)
+    {
+      priv->priority = priority;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PRIORITY]);
+    }
+}
+
+static gboolean
+ide_simple_build_system_discovery_match (IdeSimpleBuildSystemDiscovery *self,
+                                         const gchar                   *name)
+{
+  IdeSimpleBuildSystemDiscoveryPrivate *priv = ide_simple_build_system_discovery_get_instance_private (self);
+
+  g_assert (IDE_IS_SIMPLE_BUILD_SYSTEM_DISCOVERY (self));
+  g_assert (name != NULL);
+
+  return fnmatch (priv->glob, name, 0) == 0;
+}
+
+static gboolean
+ide_simple_build_system_discovery_check_dir (IdeSimpleBuildSystemDiscovery *self,
+                                             GFile                         *directory,
+                                             GCancellable                  *cancellable)
+{
+  IdeSimpleBuildSystemDiscoveryPrivate *priv = ide_simple_build_system_discovery_get_instance_private (self);
+  g_autoptr(GFileEnumerator) enumerator = NULL;
+  gpointer infoptr;
+
+  g_assert (IDE_IS_SIMPLE_BUILD_SYSTEM_DISCOVERY (self));
+  g_assert (G_IS_FILE (directory));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  if (priv->is_exact)
+    {
+      g_autoptr(GFile) child = g_file_get_child (directory, priv->glob);
+      return g_file_query_exists (child, cancellable);
+    }
+
+  enumerator = g_file_enumerate_children (directory,
+                                          G_FILE_ATTRIBUTE_STANDARD_NAME,
+                                          G_FILE_QUERY_INFO_NONE,
+                                          cancellable,
+                                          NULL);
+
+  while ((infoptr = g_file_enumerator_next_file (enumerator, cancellable, NULL)))
+    {
+      g_autoptr(GFileInfo) info = infoptr;
+      const gchar *name = g_file_info_get_name (info);
+
+      if (fnmatch (priv->glob, name, 0) == 0)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gchar *
+ide_simple_build_system_discovery_discover (IdeBuildSystemDiscovery  *discovery,
+                                            GFile                    *file,
+                                            GCancellable             *cancellable,
+                                            gint                     *priority,
+                                            GError                  **error)
+{
+  IdeSimpleBuildSystemDiscovery *self = (IdeSimpleBuildSystemDiscovery *)discovery;
+  IdeSimpleBuildSystemDiscoveryPrivate *priv = ide_simple_build_system_discovery_get_instance_private (self);
+  g_autoptr(IdeContext) context = NULL;
+  g_autoptr(GFile) directory = NULL;
+  g_autoptr(GFile) workdir = NULL;
+  g_autofree gchar *name = NULL;
+
+  g_assert (!IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_SIMPLE_BUILD_SYSTEM_DISCOVERY (self));
+  g_assert (G_IS_FILE (file));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_assert (priority != NULL);
+
+  *priority = priv->priority;
+
+  if (priv->glob == NULL || priv->hint == NULL)
+    goto failure;
+
+  name = g_file_get_basename (file);
+  if (ide_simple_build_system_discovery_match (self, name))
+    return g_strdup (priv->hint);
+
+  context = ide_object_ref_context (IDE_OBJECT (self));
+  workdir = ide_context_ref_workdir (context);
+
+  if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, cancellable) != G_FILE_TYPE_DIRECTORY)
+    file = directory = g_file_get_parent (file);
+
+  if (ide_simple_build_system_discovery_check_dir (self, file, cancellable) ||
+      ide_simple_build_system_discovery_check_dir (self, workdir, cancellable))
+    return g_strdup (priv->hint);
+
+failure:
+  g_set_error (error,
+               G_IO_ERROR,
+               G_IO_ERROR_NOT_SUPPORTED,
+               "No match was discovered");
+  return NULL;
+}
diff --git a/src/libide/foundry/ide-simple-build-system-discovery.h 
b/src/libide/foundry/ide-simple-build-system-discovery.h
new file mode 100644
index 000000000..93e953942
--- /dev/null
+++ b/src/libide/foundry/ide-simple-build-system-discovery.h
@@ -0,0 +1,62 @@
+/* ide-simple-build-system-discovery.h
+ *
+ * Copyright 2018-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
+#include <libide-code.h>
+
+#include "ide-build-system-discovery.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SIMPLE_BUILD_SYSTEM_DISCOVERY (ide_simple_build_system_discovery_get_type())
+
+IDE_AVAILABLE_IN_3_32
+G_DECLARE_DERIVABLE_TYPE (IdeSimpleBuildSystemDiscovery, ide_simple_build_system_discovery, IDE, 
SIMPLE_BUILD_SYSTEM_DISCOVERY, IdeObject)
+
+struct _IdeSimpleBuildSystemDiscoveryClass
+{
+  IdeObjectClass parent_class;
+
+  /*< private >*/
+  gpointer _reserved[8];
+};
+
+IDE_AVAILABLE_IN_3_32
+const gchar *ide_simple_build_system_discovery_get_glob     (IdeSimpleBuildSystemDiscovery *self);
+IDE_AVAILABLE_IN_3_32
+void         ide_simple_build_system_discovery_set_glob     (IdeSimpleBuildSystemDiscovery *self,
+                                                             const gchar                   *glob);
+IDE_AVAILABLE_IN_3_32
+const gchar *ide_simple_build_system_discovery_get_hint     (IdeSimpleBuildSystemDiscovery *self);
+IDE_AVAILABLE_IN_3_32
+void         ide_simple_build_system_discovery_set_hint     (IdeSimpleBuildSystemDiscovery *self,
+                                                             const gchar                   *hint);
+IDE_AVAILABLE_IN_3_32
+gint         ide_simple_build_system_discovery_get_priority (IdeSimpleBuildSystemDiscovery *self);
+IDE_AVAILABLE_IN_3_32
+void         ide_simple_build_system_discovery_set_priority (IdeSimpleBuildSystemDiscovery *self,
+                                                             gint                           priority);
+
+G_END_DECLS
diff --git a/src/libide/buildsystem/ide-simple-build-target.c b/src/libide/foundry/ide-simple-build-target.c
similarity index 98%
rename from src/libide/buildsystem/ide-simple-build-target.c
rename to src/libide/foundry/ide-simple-build-target.c
index 069ece9ac..31909a45b 100644
--- a/src/libide/buildsystem/ide-simple-build-target.c
+++ b/src/libide/foundry/ide-simple-build-target.c
@@ -22,8 +22,8 @@
 
 #include "config.h"
 
-#include "buildsystem/ide-build-target.h"
-#include "buildsystem/ide-simple-build-target.h"
+#include "ide-build-target.h"
+#include "ide-simple-build-target.h"
 
 typedef struct
 {
@@ -74,7 +74,6 @@ IdeSimpleBuildTarget *
 ide_simple_build_target_new (IdeContext *context)
 {
   return g_object_new (IDE_TYPE_SIMPLE_BUILD_TARGET,
-                       "context", context,
                        NULL);
 }
 
diff --git a/src/libide/buildsystem/ide-simple-build-target.h b/src/libide/foundry/ide-simple-build-target.h
similarity index 92%
rename from src/libide/buildsystem/ide-simple-build-target.h
rename to src/libide/foundry/ide-simple-build-target.h
index 50c4284a6..67a65727d 100644
--- a/src/libide/buildsystem/ide-simple-build-target.h
+++ b/src/libide/foundry/ide-simple-build-target.h
@@ -20,8 +20,13 @@
 
 #pragma once
 
-#include "ide-object.h"
-#include "ide-version-macros.h"
+#if !defined (IDE_FOUNDRY_INSIDE) && !defined (IDE_FOUNDRY_COMPILATION)
+# error "Only <libide-foundry.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+#include "ide-foundry-types.h"
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/buildsystem/ide-environment-variable.c 
b/src/libide/threading/ide-environment-variable.c
similarity index 99%
rename from src/libide/buildsystem/ide-environment-variable.c
rename to src/libide/threading/ide-environment-variable.c
index 32f105050..812a31449 100644
--- a/src/libide/buildsystem/ide-environment-variable.c
+++ b/src/libide/threading/ide-environment-variable.c
@@ -22,7 +22,7 @@
 
 #include "config.h"
 
-#include "buildsystem/ide-environment-variable.h"
+#include "ide-environment-variable.h"
 
 struct _IdeEnvironmentVariable
 {
diff --git a/src/libide/buildsystem/ide-environment-variable.h 
b/src/libide/threading/ide-environment-variable.h
similarity index 91%
rename from src/libide/buildsystem/ide-environment-variable.h
rename to src/libide/threading/ide-environment-variable.h
index 3fda2e51e..a58d90ddd 100644
--- a/src/libide/buildsystem/ide-environment-variable.h
+++ b/src/libide/threading/ide-environment-variable.h
@@ -20,9 +20,11 @@
 
 #pragma once
 
-#include <glib-object.h>
+#if !defined (IDE_THREADING_INSIDE) && !defined (IDE_THREADING_COMPILATION)
+# error "Only <libide-threading.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <libide-core.h>
 
 G_BEGIN_DECLS
 
diff --git a/src/libide/buildsystem/ide-environment.c b/src/libide/threading/ide-environment.c
similarity index 91%
rename from src/libide/buildsystem/ide-environment.c
rename to src/libide/threading/ide-environment.c
index 436e4afd0..41f8e6350 100644
--- a/src/libide/buildsystem/ide-environment.c
+++ b/src/libide/threading/ide-environment.c
@@ -22,8 +22,10 @@
 
 #include "config.h"
 
-#include "buildsystem/ide-environment.h"
-#include "buildsystem/ide-environment-variable.h"
+#include <libide-core.h>
+
+#include "ide-environment.h"
+#include "ide-environment-variable.h"
 
 struct _IdeEnvironment
 {
@@ -334,3 +336,44 @@ ide_environment_copy_into (IdeEnvironment *self,
         ide_environment_setenv (dest, key, value);
     }
 }
+
+/**
+ * ide_environ_parse:
+ * @pair: the KEY=VALUE pair
+ * @key: (out) (optional): a location for a @key
+ * @value: (out) (optional): a location for a @value
+ *
+ * Parses a KEY=VALUE style key-pair into @key and @value.
+ *
+ * Returns: %TRUE if @pair was successfully parsed
+ *
+ * Since: 3.32
+ */
+gboolean
+ide_environ_parse (const gchar  *pair,
+                   gchar       **key,
+                   gchar       **value)
+{
+  const gchar *eq;
+
+  g_return_val_if_fail (pair != NULL, FALSE);
+
+  if (key != NULL)
+    *key = NULL;
+
+  if (value != NULL)
+    *value = NULL;
+
+  if ((eq = strchr (pair, '=')))
+    {
+      if (key != NULL)
+        *key = g_strndup (pair, eq - pair);
+
+      if (value != NULL)
+        *value = g_strdup (eq + 1);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
diff --git a/src/libide/buildsystem/ide-environment.h b/src/libide/threading/ide-environment.h
similarity index 83%
rename from src/libide/buildsystem/ide-environment.h
rename to src/libide/threading/ide-environment.h
index ef881615e..597b80ab1 100644
--- a/src/libide/buildsystem/ide-environment.h
+++ b/src/libide/threading/ide-environment.h
@@ -20,11 +20,14 @@
 
 #pragma once
 
-#include <gio/gio.h>
+#if !defined (IDE_THREADING_INSIDE) && !defined (IDE_THREADING_COMPILATION)
+# error "Only <libide-threading.h> can be included directly."
+#endif
 
-#include "ide-version-macros.h"
+#include <gio/gio.h>
+#include <libide-core.h>
 
-#include "buildsystem/ide-environment-variable.h"
+#include "ide-environment-variable.h"
 
 G_BEGIN_DECLS
 
@@ -33,6 +36,10 @@ G_BEGIN_DECLS
 IDE_AVAILABLE_IN_3_32
 G_DECLARE_FINAL_TYPE (IdeEnvironment, ide_environment, IDE, ENVIRONMENT, GObject)
 
+IDE_AVAILABLE_IN_3_32
+gboolean        ide_environ_parse           (const gchar            *pair,
+                                             gchar                 **key,
+                                             gchar                 **value);
 IDE_AVAILABLE_IN_3_32
 IdeEnvironment *ide_environment_new         (void);
 IDE_AVAILABLE_IN_3_32



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