[gnome-builder/wip/chergert/buildcleanup] wip: refactoring of various build commands



commit 71f2fe52bd6b7fb51a8a7409533049a4ac5df79c
Author: Christian Hergert <chergert redhat com>
Date:   Sat Dec 10 00:22:34 2016 -0800

    wip: refactoring of various build commands

 .buildconfig                                   |    4 +-
 libide/Makefile.am                             |    2 +
 libide/buildsystem/ide-build-system.c          |  248 +++++++-----
 libide/buildsystem/ide-build-system.h          |   45 +--
 libide/buildsystem/ide-builder.c               |  197 +++++++++-
 libide/buildsystem/ide-builder.h               |  100 +++--
 libide/buildsystem/ide-configuration.c         |    8 +
 libide/buildsystem/ide-simple-builder.c        |  172 ++++++++
 libide/buildsystem/ide-simple-builder.h        |   47 +++
 libide/directory/ide-directory-build-system.c  |   84 +---
 libide/ide.h                                   |    1 +
 libide/runner/ide-run-manager.c                |   33 ++-
 libide/subprocess/ide-subprocess-launcher.c    |   13 +-
 plugins/autotools/ide-autotools-build-system.c |  510 ++----------------------
 plugins/autotools/ide-autotools-build-task.c   |   61 ++--
 plugins/autotools/ide-autotools-builder.c      |  502 +++++++++++++++++++++++-
 plugins/autotools/ide-makecache.c              |   98 +++--
 plugins/autotools/ide-makecache.h              |    4 +-
 plugins/cargo/cargo_plugin.py                  |   47 ++-
 plugins/git/ide-git-vcs.c                      |   22 +
 plugins/meson/meson_plugin/__init__.py         |  192 ++++-----
 tests/Makefile.am                              |   15 +-
 tests/data/project1/.gitignore                 |   18 +
 tests/data/project1/Makefile.am                |    5 +
 tests/data/project1/autogen.sh                 |   10 +
 tests/data/project1/build-aux/.gitignore       |    1 +
 tests/data/project1/configure.ac               |   10 +
 tests/test-ide-buffer-manager.c                |    2 +-
 tests/test-ide-buffer.c                        |   31 +--
 tests/test-ide-builder.c                       |  354 ++++++++++++++++
 tests/test-ide-context.c                       |    2 +-
 tests/test-ide-subprocess-launcher.c           |   33 ++-
 32 files changed, 1897 insertions(+), 974 deletions(-)
---
diff --git a/.buildconfig b/.buildconfig
index ca675d6..619490e 100644
--- a/.buildconfig
+++ b/.buildconfig
@@ -1,8 +1,8 @@
 [default]
 name=Builder (Development)
 device=local
-runtime=org.gnome.Builder.json@c316fdc083822fd726db1bb38207d31ab63bd6aa
+runtime=jhbuild
 config-opts=--enable-tracing --enable-debug --enable-hello-cpp-plugin
-prefix=/app
+prefix=/opt/gnome
 app-id=org.gnome.Builder
 default=true
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 833a67e..987d890 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -42,6 +42,7 @@ libide_1_0_la_public_headers =                            \
        buildsystem/ide-configuration.h                   \
        buildsystem/ide-environment-variable.h            \
        buildsystem/ide-environment.h                     \
+       buildsystem/ide-simple-builder.h                  \
        devices/ide-device-manager.h                      \
        devices/ide-device-provider.h                     \
        devices/ide-device.h                              \
@@ -216,6 +217,7 @@ libide_1_0_la_public_sources =                            \
        buildsystem/ide-configuration.c                   \
        buildsystem/ide-environment-variable.c            \
        buildsystem/ide-environment.c                     \
+       buildsystem/ide-simple-builder.c                  \
        devices/ide-device-manager.c                      \
        devices/ide-device-provider.c                     \
        devices/ide-device.c                              \
diff --git a/libide/buildsystem/ide-build-system.c b/libide/buildsystem/ide-build-system.c
index 546bd58..b5a9c1a 100644
--- a/libide/buildsystem/ide-build-system.c
+++ b/libide/buildsystem/ide-build-system.c
@@ -18,23 +18,14 @@
 
 #define G_LOG_DOMAIN "ide-build-system"
 
-#include <glib/gi18n.h>
-
 #include "ide-context.h"
 #include "ide-object.h"
 
 #include "buildsystem/ide-build-system.h"
+#include "buildsystem/ide-builder.h"
 #include "buildsystem/ide-configuration.h"
-#include "devices/ide-device.h"
-#include "devices/ide-device-manager.h"
+#include "buildsystem/ide-configuration-manager.h"
 #include "files/ide-file.h"
-#include "runtimes/ide-runtime.h"
-#include "runtimes/ide-runtime-manager.h"
-
-typedef struct
-{
-  GFile *project_file;
-} IdeBuildSystemPrivate;
 
 G_DEFINE_INTERFACE (IdeBuildSystem, ide_build_system, IDE_TYPE_OBJECT)
 
@@ -42,10 +33,10 @@ enum {
   PROP_0,
   PROP_CONTEXT,
   PROP_PROJECT_FILE,
-  LAST_PROP
+  N_PROPS
 };
 
-static GParamSpec *properties [LAST_PROP];
+static GParamSpec *properties [N_PROPS];
 
 gint
 ide_build_system_get_priority (IdeBuildSystem *self)
@@ -62,55 +53,6 @@ ide_build_system_get_priority (IdeBuildSystem *self)
   return 0;
 }
 
-/**
- * ide_build_system_get_build_flags_async:
- *
- * Asynchronously requests the build flags for a file. For autotools and C based projects, this
- * would be similar to the $CFLAGS variable and is suitable for generating warnings and errors
- * with clang.
- */
-void
-ide_build_system_get_build_flags_async (IdeBuildSystem      *self,
-                                        IdeFile             *file,
-                                        GCancellable        *cancellable,
-                                        GAsyncReadyCallback  callback,
-                                        gpointer             user_data)
-{
-  g_autoptr(GTask) task = NULL;
-
-  g_return_if_fail (IDE_IS_BUILD_SYSTEM (self));
-  g_return_if_fail (IDE_IS_FILE (file));
-  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  if (IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_flags_async)
-    return IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_flags_async (self, file, cancellable,
-                                                                     callback, user_data);
-
-  task = g_task_new (self, cancellable, callback, user_data);
-  g_task_return_pointer (task, NULL, NULL);
-}
-
-/**
- * ide_build_system_get_build_flags_finish:
- *
- * Completes an asynchronous request to get the build flags for a file.
- *
- * Returns: (array zero-terminated=1) (transfer full): An array of strings
- *   containing the build flags, or %NULL upon failure and @error is set.
- */
-gchar **
-ide_build_system_get_build_flags_finish (IdeBuildSystem  *self,
-                                         GAsyncResult    *result,
-                                         GError         **error)
-{
-  g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
-
-  if (IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_flags_finish)
-    return IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_flags_finish (self, result, error);
-
-  return g_new0 (gchar*, 1);
-}
-
 static IdeBuilder *
 ide_build_system_real_get_builder (IdeBuildSystem    *self,
                                    IdeConfiguration  *configuration,
@@ -122,49 +64,16 @@ ide_build_system_real_get_builder (IdeBuildSystem    *self,
   g_set_error (error,
                G_IO_ERROR,
                G_IO_ERROR_NOT_SUPPORTED,
-               _("%s() is not supported on %s build system."),
-               G_STRFUNC, g_type_name (G_TYPE_FROM_INSTANCE (self)));
+               "%s() is not supported on %s build system.",
+               G_STRFUNC, G_OBJECT_TYPE_NAME (self));
 
   return NULL;
 }
 
 static void
-ide_build_system_real_get_build_targets_async (IdeBuildSystem      *self,
-                                               GCancellable        *cancellable,
-                                               GAsyncReadyCallback  callback,
-                                               gpointer             user_data)
-{
-  g_autoptr(GTask) task = NULL;
-
-  g_assert (IDE_IS_BUILD_SYSTEM (self));
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  task = g_task_new (self, cancellable, callback, user_data);
-  g_task_set_source_tag (task, ide_build_system_real_get_build_targets_async);
-  g_task_return_pointer (task, g_ptr_array_new (), (GDestroyNotify)g_ptr_array_unref);
-}
-
-static GPtrArray *
-ide_build_system_real_get_build_targets_finish (IdeBuildSystem  *self,
-                                                GAsyncResult    *result,
-                                                GError         **error)
-{
-  GTask *task = (GTask *)result;
-
-  g_assert (IDE_IS_BUILD_SYSTEM (self));
-  g_assert (G_IS_TASK (task));
-  g_assert (g_task_is_valid (task, self));
-  g_assert (g_task_get_source_tag (task) ==  ide_build_system_real_get_build_targets_async);
-
-  return g_task_propagate_pointer (task, error);
-}
-
-static void
 ide_build_system_default_init (IdeBuildSystemInterface *iface)
 {
   iface->get_builder = ide_build_system_real_get_builder;
-  iface->get_build_targets_async = ide_build_system_real_get_build_targets_async;
-  iface->get_build_targets_finish = ide_build_system_real_get_build_targets_finish;
 
   properties [PROP_PROJECT_FILE] =
     g_param_spec_object ("project-file",
@@ -269,10 +178,127 @@ ide_build_system_get_builder (IdeBuildSystem    *system,
                               IdeConfiguration  *configuration,
                               GError           **error)
 {
+  IdeBuilder *ret;
+
   g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (system), NULL);
   g_return_val_if_fail (IDE_IS_CONFIGURATION (configuration), NULL);
 
-  return IDE_BUILD_SYSTEM_GET_IFACE (system)->get_builder (system, configuration, error);
+  ret = IDE_BUILD_SYSTEM_GET_IFACE (system)->get_builder (system, configuration, error);
+
+  if (ret != NULL)
+    {
+      IdeContext *context;
+
+      context = ide_object_get_context (IDE_OBJECT (system));
+      ide_context_hold_for_object (context, ret);
+    }
+
+  return ret;
+}
+
+static IdeBuilder *
+get_default_builder (IdeBuildSystem  *self,
+                     GError         **error)
+{
+  IdeConfigurationManager *config_manager;
+  IdeConfiguration *config;
+  IdeContext *context;
+
+  g_assert (IDE_IS_BUILD_SYSTEM (self));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  g_assert (IDE_IS_CONTEXT (context));
+
+  config_manager = ide_context_get_configuration_manager (context);
+  g_assert (IDE_IS_CONFIGURATION_MANAGER (config_manager));
+
+  config = ide_configuration_manager_get_current (config_manager);
+  g_assert (IDE_IS_CONFIGURATION (config));
+
+  return ide_build_system_get_builder (IDE_BUILD_SYSTEM (self), config, error);
+}
+
+static void
+ide_build_system_get_build_flags_cb (GObject      *object,
+                                     GAsyncResult *result,
+                                     gpointer      user_data)
+{
+  IdeBuilder *builder = (IdeBuilder *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_auto(GStrv) flags = NULL;
+
+  g_assert (IDE_IS_BUILDER (builder));
+
+  if (NULL == (flags = ide_builder_get_build_flags_finish (builder, result, &error)))
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_pointer (task, g_steal_pointer (&flags), (GDestroyNotify)g_strfreev);
+}
+
+void
+ide_build_system_get_build_flags_async (IdeBuildSystem      *self,
+                                        IdeFile             *file,
+                                        GCancellable        *cancellable,
+                                        GAsyncReadyCallback  callback,
+                                        gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(IdeBuilder) builder = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_return_if_fail (IDE_IS_BUILD_SYSTEM (self));
+  g_return_if_fail (IDE_IS_FILE (file));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_build_system_get_build_flags_async);
+
+  if (NULL == (builder = get_default_builder (self, &error)))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  ide_builder_get_build_flags_async (builder,
+                                     file,
+                                     cancellable,
+                                     ide_build_system_get_build_flags_cb,
+                                     g_steal_pointer (&task));
+}
+
+/**
+ * ide_build_system_get_build_flags_finish:
+ *
+ * Returns: (transfer full):
+ */
+gchar **
+ide_build_system_get_build_flags_finish (IdeBuildSystem  *self,
+                                         GAsyncResult    *result,
+                                         GError         **error)
+{
+  g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
+  g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_build_system_get_build_targets_cb (GObject      *object,
+                                       GAsyncResult *result,
+                                       gpointer      user_data)
+{
+  IdeBuilder *builder = (IdeBuilder *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GPtrArray) targets = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_BUILDER (builder));
+
+  if (NULL == (targets = ide_builder_get_build_targets_finish (builder, result, &error)))
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_pointer (task, g_steal_pointer (&targets), (GDestroyNotify)g_ptr_array_unref);
 }
 
 void
@@ -281,17 +307,33 @@ ide_build_system_get_build_targets_async (IdeBuildSystem      *self,
                                           GAsyncReadyCallback  callback,
                                           gpointer             user_data)
 {
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(IdeBuilder) builder = NULL;
+  g_autoptr(GError) error = NULL;
+
   g_return_if_fail (IDE_IS_BUILD_SYSTEM (self));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_targets_async (self, cancellable, callback, user_data);
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_build_system_get_build_targets_async);
+
+  if (NULL == (builder = get_default_builder (self, &error)))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  ide_builder_get_build_targets_async (builder,
+                                       cancellable,
+                                       ide_build_system_get_build_targets_cb,
+                                       g_steal_pointer (&task));
 }
 
 /**
  * ide_build_system_get_build_targets_finish:
  *
- * Returns: (transfer container) (element-type Ide.BuildTarget): An array of build targets
- *   or %NULL upon failure and @error is set.
+ * Returns: (transfer container) (element-type Ide.BuildTarget): An array
+ *   of #IdeBuildTarget or %NULL and @error is set.
  */
 GPtrArray *
 ide_build_system_get_build_targets_finish (IdeBuildSystem  *self,
@@ -299,7 +341,7 @@ ide_build_system_get_build_targets_finish (IdeBuildSystem  *self,
                                            GError         **error)
 {
   g_return_val_if_fail (IDE_IS_BUILD_SYSTEM (self), NULL);
-  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+  g_return_val_if_fail (G_IS_TASK (result), NULL);
 
-  return IDE_BUILD_SYSTEM_GET_IFACE (self)->get_build_targets_finish (self, result, error);
+  return g_task_propagate_pointer (G_TASK (result), error);
 }
diff --git a/libide/buildsystem/ide-build-system.h b/libide/buildsystem/ide-build-system.h
index 760a0ba..b1b779e 100644
--- a/libide/buildsystem/ide-build-system.h
+++ b/libide/buildsystem/ide-build-system.h
@@ -33,36 +33,13 @@ struct _IdeBuildSystemInterface
 {
   GTypeInterface parent_iface;
 
-  gint             (*get_priority)             (IdeBuildSystem       *system);
-  IdeBuilder      *(*get_builder)              (IdeBuildSystem       *system,
-                                                IdeConfiguration     *configuration,
-                                                GError              **error);
-  void             (*get_build_flags_async)    (IdeBuildSystem       *self,
-                                                IdeFile              *file,
-                                                GCancellable         *cancellable,
-                                                GAsyncReadyCallback   callback,
-                                                gpointer              user_data);
-  gchar          **(*get_build_flags_finish)   (IdeBuildSystem       *self,
-                                                GAsyncResult         *result,
-                                                GError              **error);
-  void             (*get_build_targets_async)  (IdeBuildSystem       *self,
-                                                GCancellable         *cancellable,
-                                                GAsyncReadyCallback   callback,
-                                                gpointer              user_data);
-  GPtrArray       *(*get_build_targets_finish) (IdeBuildSystem       *self,
-                                                GAsyncResult         *result,
-                                                GError              **error);
+  gint             (*get_priority) (IdeBuildSystem       *system);
+  IdeBuilder      *(*get_builder)  (IdeBuildSystem       *system,
+                                    IdeConfiguration     *configuration,
+                                    GError              **error);
 };
 
 gint            ide_build_system_get_priority             (IdeBuildSystem       *self);
-void            ide_build_system_get_build_flags_async    (IdeBuildSystem       *self,
-                                                           IdeFile              *file,
-                                                           GCancellable         *cancellable,
-                                                           GAsyncReadyCallback   callback,
-                                                           gpointer              user_data);
-gchar         **ide_build_system_get_build_flags_finish   (IdeBuildSystem       *self,
-                                                           GAsyncResult         *result,
-                                                           GError              **error);
 void            ide_build_system_new_async                (IdeContext           *context,
                                                            GFile                *project_file,
                                                            GCancellable         *cancellable,
@@ -73,6 +50,20 @@ IdeBuildSystem *ide_build_system_new_finish               (GAsyncResult
 IdeBuilder     *ide_build_system_get_builder              (IdeBuildSystem       *system,
                                                            IdeConfiguration     *configuration,
                                                            GError              **error);
+
+/*
+ * The following is convenience API for the legacy design to allow
+ * querying using the current IdeConfiguration.
+ */
+
+void            ide_build_system_get_build_flags_async    (IdeBuildSystem       *self,
+                                                           IdeFile              *file,
+                                                           GCancellable         *cancellable,
+                                                           GAsyncReadyCallback   callback,
+                                                           gpointer              user_data);
+gchar         **ide_build_system_get_build_flags_finish   (IdeBuildSystem       *self,
+                                                           GAsyncResult         *result,
+                                                           GError              **error);
 void            ide_build_system_get_build_targets_async  (IdeBuildSystem       *self,
                                                            GCancellable         *cancellable,
                                                            GAsyncReadyCallback   callback,
diff --git a/libide/buildsystem/ide-builder.c b/libide/buildsystem/ide-builder.c
index d428fd7..24a14a5 100644
--- a/libide/buildsystem/ide-builder.c
+++ b/libide/buildsystem/ide-builder.c
@@ -16,11 +16,16 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define G_LOG_DOMAIN "ide-builder"
+
 #include <glib/gi18n.h>
 
-#include "ide-build-result.h"
-#include "ide-builder.h"
-#include "ide-configuration.h"
+#include "ide-context.h"
+#include "ide-debug.h"
+
+#include "buildsystem/ide-build-result.h"
+#include "buildsystem/ide-builder.h"
+#include "buildsystem/ide-configuration.h"
 
 typedef struct
 {
@@ -63,9 +68,14 @@ ide_builder_set_configuration (IdeBuilder       *self,
 
   g_assert (IDE_IS_BUILDER (self));
   g_assert (!configuration || IDE_IS_CONFIGURATION (configuration));
+  g_assert (priv->configuration == NULL);
+
+  /* Make a copy of the configuration so that we do not need to worry
+   * about the user modifying the configuration while our bulid is
+   * active (and may be running in another thread).
+   */
 
-  if (g_set_object (&priv->configuration, configuration))
-    g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CONFIGURATION]);
+  priv->configuration = ide_configuration_duplicate (configuration);
 }
 
 static void
@@ -83,6 +93,8 @@ ide_builder_real_build_async (IdeBuilder            *self,
   g_assert (!result || *result == NULL);
 
   task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_builder_real_build_async);
+
   g_task_return_new_error (task,
                            G_IO_ERROR,
                            G_IO_ERROR_NOT_SUPPORTED,
@@ -101,6 +113,80 @@ ide_builder_real_build_finish (IdeBuilder    *self,
   return g_task_propagate_pointer (G_TASK (result), error);
 }
 
+static void
+ide_builder_real_get_build_targets_async (IdeBuilder          *self,
+                                          GCancellable        *cancellable,
+                                          GAsyncReadyCallback  callback,
+                                          gpointer             user_data)
+{
+  g_task_report_new_error (self,
+                           callback,
+                           user_data,
+                           ide_builder_real_get_build_targets_async,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "build targets not supported for %s",
+                           G_OBJECT_TYPE_NAME (self));
+}
+
+static GPtrArray *
+ide_builder_real_get_build_targets_finish (IdeBuilder    *self,
+                                           GAsyncResult  *result,
+                                           GError       **error)
+{
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_builder_real_get_build_flags_async (IdeBuilder          *self,
+                                        IdeFile             *file,
+                                        GCancellable        *cancellable,
+                                        GAsyncReadyCallback  callback,
+                                        gpointer             user_data)
+{
+  g_task_report_new_error (self,
+                           callback,
+                           user_data,
+                           ide_builder_real_get_build_flags_async,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "build flags not supported for %s",
+                           G_OBJECT_TYPE_NAME (self));
+}
+
+static gchar **
+ide_builder_real_get_build_flags_finish (IdeBuilder    *self,
+                                         GAsyncResult  *result,
+                                         GError       **error)
+{
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_builder_real_install_async (IdeBuilder          *self,
+                                IdeBuildResult     **build_result,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  g_task_report_new_error (self,
+                           callback,
+                           user_data,
+                           ide_builder_real_install_async,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "install not supported for %s",
+                           G_OBJECT_TYPE_NAME (self));
+}
+
+static IdeBuildResult *
+ide_builder_real_install_finish (IdeBuilder    *self,
+                                 GAsyncResult  *result,
+                                 GError       **error)
+{
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
 /**
  * ide_builder_build_async:
  * @self: An #IdeBuilder
@@ -117,12 +203,12 @@ ide_builder_real_build_finish (IdeBuilder    *self,
  * See ide_builder_build_finish() to complete the request.
  */
 void
-ide_builder_build_async (IdeBuilder           *builder,
-                         IdeBuilderBuildFlags  flags,
-                         IdeBuildResult      **result,
-                         GCancellable         *cancellable,
-                         GAsyncReadyCallback   callback,
-                         gpointer              user_data)
+ide_builder_build_async (IdeBuilder            *builder,
+                         IdeBuilderBuildFlags   flags,
+                         IdeBuildResult       **result,
+                         GCancellable          *cancellable,
+                         GAsyncReadyCallback    callback,
+                         gpointer               user_data)
 {
   g_return_if_fail (IDE_IS_BUILDER (builder));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
@@ -196,6 +282,19 @@ ide_builder_set_property (GObject      *object,
 }
 
 static void
+ide_builder_constructed (GObject *object)
+{
+  G_OBJECT_CLASS (ide_builder_parent_class)->constructed (object);
+
+#ifdef IDE_ENABLE_TRACE
+  {
+    IdeContext *context = ide_object_get_context (IDE_OBJECT (object));
+    g_assert (IDE_IS_CONTEXT (context));
+  }
+#endif
+}
+
+static void
 ide_builder_finalize (GObject *object)
 {
   IdeBuilder *self = (IdeBuilder *)object;
@@ -211,12 +310,19 @@ ide_builder_class_init (IdeBuilderClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
+  object_class->constructed = ide_builder_constructed;
   object_class->finalize = ide_builder_finalize;
   object_class->get_property = ide_builder_get_property;
   object_class->set_property = ide_builder_set_property;
 
   klass->build_async = ide_builder_real_build_async;
   klass->build_finish = ide_builder_real_build_finish;
+  klass->install_async = ide_builder_real_install_async;
+  klass->install_finish = ide_builder_real_install_finish;
+  klass->get_build_flags_async = ide_builder_real_get_build_flags_async;
+  klass->get_build_flags_finish = ide_builder_real_get_build_flags_finish;
+  klass->get_build_targets_async = ide_builder_real_get_build_targets_async;
+  klass->get_build_targets_finish = ide_builder_real_get_build_targets_finish;
 
   properties [PROP_CONFIGURATION] =
     g_param_spec_object ("configuration",
@@ -284,3 +390,72 @@ ide_builder_install_finish (IdeBuilder    *self,
 
   return ret;
 }
+
+void
+ide_builder_get_build_targets_async (IdeBuilder          *self,
+                                     GCancellable        *cancellable,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             user_data)
+{
+  g_return_if_fail (IDE_IS_BUILDER (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  IDE_BUILDER_GET_CLASS (self)->get_build_targets_async (self, cancellable, callback, user_data);
+}
+
+/**
+ * ide_builder_get_build_targets_finish:
+ * @self: An #IdeBuilder
+ * @result: A #GAsyncResult provided to the async callback
+ * @error: A location for a #GError or %NULL
+ *
+ * Completes an async operation to ide_builder_get_build_targets_async().
+ *
+ * Returns: (transfer container) (element-type Ide.BuildTarget): A #GPtrArray of the
+ *   build targets or %NULL upon failure and @error is set.
+ */
+GPtrArray *
+ide_builder_get_build_targets_finish (IdeBuilder    *self,
+                                      GAsyncResult  *result,
+                                      GError       **error)
+{
+  g_return_val_if_fail (IDE_IS_BUILDER (self), NULL);
+  g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+  return IDE_BUILDER_GET_CLASS (self)->get_build_targets_finish (self, result, error);
+}
+
+void
+ide_builder_get_build_flags_async (IdeBuilder          *self,
+                                   IdeFile             *file,
+                                   GCancellable        *cancellable,
+                                   GAsyncReadyCallback  callback,
+                                   gpointer             user_data)
+{
+  g_return_if_fail (IDE_IS_BUILDER (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  IDE_BUILDER_GET_CLASS (self)->get_build_flags_async (self, file, cancellable, callback, user_data);
+}
+
+/**
+ * ide_builder_get_build_flags_finish:
+ * @self: An #IdeBuilder
+ * @result: A #GAsyncResult provided to the async callback
+ * @error: A location for a #GError, or %NULL
+ *
+ * Completes the async operation to ide_builder_get_build_flags_async()
+ *
+ * Returns: (transfer full): A newly allocated %NULL terminated array of strings,
+ *   or %NULL upon failure.
+ */
+gchar **
+ide_builder_get_build_flags_finish (IdeBuilder    *self,
+                                    GAsyncResult  *result,
+                                    GError       **error)
+{
+  g_return_val_if_fail (IDE_IS_BUILDER (self), NULL);
+  g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+  return IDE_BUILDER_GET_CLASS (self)->get_build_flags_finish (self, result, error);
+}
diff --git a/libide/buildsystem/ide-builder.h b/libide/buildsystem/ide-builder.h
index 11e240a..8a8c6de 100644
--- a/libide/buildsystem/ide-builder.h
+++ b/libide/buildsystem/ide-builder.h
@@ -40,23 +40,38 @@ struct _IdeBuilderClass
 {
   IdeObjectClass parent;
 
-  void            (*build_async)    (IdeBuilder            *self,
-                                     IdeBuilderBuildFlags   flags,
-                                     IdeBuildResult       **result,
-                                     GCancellable          *cancellable,
-                                     GAsyncReadyCallback    callback,
-                                     gpointer               user_data);
-  IdeBuildResult *(*build_finish)   (IdeBuilder            *self,
-                                     GAsyncResult          *result,
-                                     GError               **error);
-  void            (*install_async)  (IdeBuilder            *self,
-                                     IdeBuildResult       **result,
-                                     GCancellable          *cancellable,
-                                     GAsyncReadyCallback    callback,
-                                     gpointer               user_data);
-  IdeBuildResult *(*install_finish) (IdeBuilder            *self,
-                                     GAsyncResult          *result,
-                                     GError               **error);
+  void             (*build_async)              (IdeBuilder            *self,
+                                                IdeBuilderBuildFlags   flags,
+                                                IdeBuildResult       **result,
+                                                GCancellable          *cancellable,
+                                                GAsyncReadyCallback    callback,
+                                                gpointer               user_data);
+  IdeBuildResult  *(*build_finish)             (IdeBuilder            *self,
+                                                GAsyncResult          *result,
+                                                GError               **error);
+  void             (*install_async)            (IdeBuilder            *self,
+                                                IdeBuildResult       **result,
+                                                GCancellable          *cancellable,
+                                                GAsyncReadyCallback    callback,
+                                                gpointer               user_data);
+  IdeBuildResult  *(*install_finish)           (IdeBuilder            *self,
+                                                GAsyncResult          *result,
+                                                GError               **error);
+  void             (*get_build_flags_async)    (IdeBuilder            *self,
+                                                IdeFile               *file,
+                                                GCancellable          *cancellable,
+                                                GAsyncReadyCallback    callback,
+                                                gpointer               user_data);
+  gchar          **(*get_build_flags_finish)   (IdeBuilder            *self,
+                                                GAsyncResult          *result,
+                                                GError               **error);
+  void             (*get_build_targets_async)  (IdeBuilder            *self,
+                                                GCancellable          *cancellable,
+                                                GAsyncReadyCallback    callback,
+                                                gpointer               user_data);
+  GPtrArray       *(*get_build_targets_finish) (IdeBuilder            *self,
+                                                GAsyncResult          *result,
+                                                GError               **error);
 
   gpointer _reserved1;
   gpointer _reserved2;
@@ -72,24 +87,39 @@ struct _IdeBuilderClass
   gpointer _reserved12;
 };
 
-IdeConfiguration *ide_builder_get_configuration (IdeBuilder            *self);
-void              ide_builder_build_async       (IdeBuilder            *self,
-                                                 IdeBuilderBuildFlags   flags,
-                                                 IdeBuildResult       **result,
-                                                 GCancellable          *cancellable,
-                                                 GAsyncReadyCallback    callback,
-                                                 gpointer               user_data);
-IdeBuildResult   *ide_builder_build_finish      (IdeBuilder            *self,
-                                                 GAsyncResult          *result,
-                                                 GError               **error);
-void              ide_builder_install_async     (IdeBuilder            *self,
-                                                 IdeBuildResult       **result,
-                                                 GCancellable          *cancellable,
-                                                 GAsyncReadyCallback    callback,
-                                                 gpointer               user_data);
-IdeBuildResult   *ide_builder_install_finish    (IdeBuilder            *self,
-                                                 GAsyncResult          *result,
-                                                 GError               **error);
+IdeConfiguration *ide_builder_get_configuration        (IdeBuilder            *self);
+void              ide_builder_build_async              (IdeBuilder            *self,
+                                                        IdeBuilderBuildFlags   flags,
+                                                        IdeBuildResult       **result,
+                                                        GCancellable          *cancellable,
+                                                        GAsyncReadyCallback    callback,
+                                                        gpointer               user_data);
+IdeBuildResult   *ide_builder_build_finish             (IdeBuilder            *self,
+                                                        GAsyncResult          *result,
+                                                        GError               **error);
+void              ide_builder_install_async            (IdeBuilder            *self,
+                                                        IdeBuildResult       **result,
+                                                        GCancellable          *cancellable,
+                                                        GAsyncReadyCallback    callback,
+                                                        gpointer               user_data);
+IdeBuildResult   *ide_builder_install_finish           (IdeBuilder            *self,
+                                                        GAsyncResult          *result,
+                                                        GError               **error);
+void              ide_builder_get_build_targets_async  (IdeBuilder            *self,
+                                                        GCancellable          *cancellable,
+                                                        GAsyncReadyCallback    callback,
+                                                        gpointer               user_data);
+GPtrArray        *ide_builder_get_build_targets_finish (IdeBuilder            *self,
+                                                        GAsyncResult          *result,
+                                                        GError               **error);
+void              ide_builder_get_build_flags_async    (IdeBuilder            *self,
+                                                        IdeFile               *file,
+                                                        GCancellable          *cancellable,
+                                                        GAsyncReadyCallback    callback,
+                                                        gpointer               user_data);
+gchar           **ide_builder_get_build_flags_finish   (IdeBuilder            *self,
+                                                        GAsyncResult          *result,
+                                                        GError               **error);
 
 G_END_DECLS
 
diff --git a/libide/buildsystem/ide-configuration.c b/libide/buildsystem/ide-configuration.c
index fc458de..e749fd8 100644
--- a/libide/buildsystem/ide-configuration.c
+++ b/libide/buildsystem/ide-configuration.c
@@ -847,6 +847,8 @@ void
 ide_configuration_set_dirty (IdeConfiguration *self,
                              gboolean          dirty)
 {
+  IDE_ENTRY;
+
   g_return_if_fail (IDE_IS_CONFIGURATION (self));
 
   dirty = !!dirty;
@@ -865,8 +867,11 @@ ide_configuration_set_dirty (IdeConfiguration *self,
   if (dirty)
     {
       self->sequence++;
+      IDE_TRACE_MSG ("configuration set dirty with sequence %u", self->sequence);
       ide_configuration_emit_changed (self);
     }
+
+  IDE_EXIT;
 }
 
 /**
@@ -941,6 +946,7 @@ ide_configuration_duplicate (IdeConfiguration *self)
                        "runtime-id", self->runtime_id,
                        NULL);
 
+  copy->sequence = self->sequence;
   copy->environment = ide_environment_copy (self->environment);
 
   if (self->prebuild)
@@ -953,6 +959,8 @@ ide_configuration_duplicate (IdeConfiguration *self)
   while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
     g_hash_table_insert (copy->internal, g_strdup (key), _value_copy (value));
 
+  copy->dirty = self->dirty;
+
   return copy;
 }
 
diff --git a/libide/buildsystem/ide-simple-builder.c b/libide/buildsystem/ide-simple-builder.c
new file mode 100644
index 0000000..e16706b
--- /dev/null
+++ b/libide/buildsystem/ide-simple-builder.c
@@ -0,0 +1,172 @@
+/* ide-simple-builder.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-simple-builder"
+
+#include <gtksourceview/gtksource.h>
+
+#include "buildsystem/ide-simple-builder.h"
+#include "files/ide-file.h"
+
+G_DEFINE_TYPE (IdeSimpleBuilder, ide_simple_builder, IDE_TYPE_BUILDER)
+
+static void
+ide_simple_builder_build_async (IdeBuilder            *builder,
+                                IdeBuilderBuildFlags   flags,
+                                IdeBuildResult       **build_result,
+                                GCancellable          *cancellable,
+                                GAsyncReadyCallback    callback,
+                                gpointer               user_data)
+{
+  g_assert (IDE_IS_SIMPLE_BUILDER (builder));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  /* TODO: we can use the prebuild/postbuild commands at least? */
+
+  g_task_report_new_error (builder,
+                           callback,
+                           user_data,
+                           ide_simple_builder_build_async,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "%s does not support building",
+                           G_OBJECT_TYPE_NAME (builder));
+}
+
+static IdeBuildResult *
+ide_simple_builder_build_finish (IdeBuilder    *builder,
+                                 GAsyncResult  *result,
+                                 GError       **error)
+{
+  g_assert (IDE_IS_SIMPLE_BUILDER (builder));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_simple_builder_install_async (IdeBuilder           *builder,
+                                  IdeBuildResult      **build_result,
+                                  GCancellable         *cancellable,
+                                  GAsyncReadyCallback   callback,
+                                  gpointer              user_data)
+{
+  /* TODO: we can use the prebuild/postbuild commands at least? */
+
+  g_task_report_new_error (builder,
+                           callback,
+                           user_data,
+                           ide_simple_builder_install_async,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "%s does not support installing",
+                           G_OBJECT_TYPE_NAME (builder));
+}
+
+static IdeBuildResult *
+ide_simple_builder_install_finish (IdeBuilder    *builder,
+                                   GAsyncResult  *result,
+                                   GError       **error)
+{
+  g_assert (IDE_IS_SIMPLE_BUILDER (builder));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_simple_builder_get_build_flags_async (IdeBuilder          *builder,
+                                          IdeFile             *file,
+                                          GCancellable        *cancellable,
+                                          GAsyncReadyCallback  callback,
+                                          gpointer             user_data)
+{
+  IdeSimpleBuilder *self = (IdeSimpleBuilder *)builder;
+  g_autoptr(GTask) task = NULL;
+  IdeConfiguration *config;
+  GtkSourceLanguage *language;
+  const gchar *env = NULL;
+  const gchar *id;
+
+  g_assert (IDE_IS_SIMPLE_BUILDER (self));
+  g_assert (IDE_IS_FILE (file));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_simple_builder_get_build_flags_async);
+
+  language = ide_file_get_language (file);
+
+  config = ide_builder_get_configuration (builder);
+
+  if (config == NULL || language == NULL)
+    goto failure;
+
+  id = gtk_source_language_get_id (language);
+
+  if (ide_str_equal0 (id, "c") || ide_str_equal0 (id, "chdr"))
+    env = ide_configuration_getenv (config, "CFLAGS");
+  else if (ide_str_equal0 (id, "cpp") || ide_str_equal0 (id, "cpphdr"))
+    env = ide_configuration_getenv (config, "CXXFLAGS");
+  else if (ide_str_equal0 (id, "vala"))
+    env = ide_configuration_getenv (config, "VALAFLAGS");
+
+  if (env != NULL)
+    {
+      gchar **flags = NULL;
+      gint argc;
+
+      if (g_shell_parse_argv (env, &argc, &flags, NULL))
+        {
+          g_task_return_pointer (task, flags, (GDestroyNotify)g_strfreev);
+          return;
+        }
+    }
+
+failure:
+  g_task_return_pointer (task, g_new0 (gchar*, 1), (GDestroyNotify)g_strfreev);
+}
+
+static gchar **
+ide_simple_builder_get_build_flags_finish (IdeBuilder    *builder,
+                                           GAsyncResult  *result,
+                                           GError       **error)
+{
+  g_assert (IDE_IS_SIMPLE_BUILDER (builder));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_simple_builder_class_init (IdeSimpleBuilderClass *klass)
+{
+  IdeBuilderClass *builder_class = IDE_BUILDER_CLASS (klass);
+
+  builder_class->get_build_flags_async = ide_simple_builder_get_build_flags_async;
+  builder_class->get_build_flags_finish = ide_simple_builder_get_build_flags_finish;
+  builder_class->build_async = ide_simple_builder_build_async;
+  builder_class->build_finish = ide_simple_builder_build_finish;
+  builder_class->install_async = ide_simple_builder_install_async;
+  builder_class->install_finish = ide_simple_builder_install_finish;
+}
+
+static void
+ide_simple_builder_init (IdeSimpleBuilder *self)
+{
+}
diff --git a/libide/buildsystem/ide-simple-builder.h b/libide/buildsystem/ide-simple-builder.h
new file mode 100644
index 0000000..705ef74
--- /dev/null
+++ b/libide/buildsystem/ide-simple-builder.h
@@ -0,0 +1,47 @@
+/* ide-simple-builder.h
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_SIMPLE_BUILDER_H
+#define IDE_SIMPLE_BUILDER_H
+
+#include "buildsystem/ide-builder.h"
+#include "buildsystem/ide-configuration.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SIMPLE_BUILDER (ide_simple_builder_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeSimpleBuilder, ide_simple_builder, IDE, SIMPLE_BUILDER, IdeBuilder)
+
+struct _IdeSimpleBuilderClass
+{
+  IdeBuilderClass parent_class;
+
+  gpointer _reserved1;
+  gpointer _reserved2;
+  gpointer _reserved3;
+  gpointer _reserved4;
+  gpointer _reserved5;
+  gpointer _reserved6;
+  gpointer _reserved7;
+  gpointer _reserved8;
+};
+
+G_END_DECLS
+
+#endif /* IDE_SIMPLE_BUILDER_H */
diff --git a/libide/directory/ide-directory-build-system.c b/libide/directory/ide-directory-build-system.c
index 432ceca..7a25d9a 100644
--- a/libide/directory/ide-directory-build-system.c
+++ b/libide/directory/ide-directory-build-system.c
@@ -24,6 +24,7 @@
 
 #include "buildsystem/ide-configuration-manager.h"
 #include "buildsystem/ide-configuration.h"
+#include "buildsystem/ide-simple-builder.h"
 #include "directory/ide-directory-build-system.h"
 #include "projects/ide-project-file.h"
 #include "projects/ide-project-item.h"
@@ -169,85 +170,34 @@ async_initiable_init (GAsyncInitableIface *iface)
   iface->init_finish = ide_directory_build_system_init_finish;
 }
 
-static void
-ide_directory_build_system_get_build_flags_async (IdeBuildSystem      *build_system,
-                                                  IdeFile             *file,
-                                                  GCancellable        *cancellable,
-                                                  GAsyncReadyCallback  callback,
-                                                  gpointer             user_data)
+static gint
+ide_directory_build_system_get_priority (IdeBuildSystem *build_system)
+{
+  return 1000000;
+}
+
+static IdeBuilder *
+ide_directory_build_system_get_builder (IdeBuildSystem    *build_system,
+                                        IdeConfiguration  *configuration,
+                                        GError           **error)
 {
   IdeDirectoryBuildSystem *self = (IdeDirectoryBuildSystem *)build_system;
-  g_autoptr(GTask) task = NULL;
-  IdeConfigurationManager *configmgr;
-  GtkSourceLanguage *language;
-  IdeConfiguration *config;
   IdeContext *context;
-  const gchar *env = NULL;
-  const gchar *id;
 
   g_assert (IDE_IS_DIRECTORY_BUILD_SYSTEM (self));
-  g_assert (IDE_IS_FILE (file));
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  task = g_task_new (self, cancellable, callback, user_data);
+  g_assert (IDE_IS_CONFIGURATION (configuration));
 
   context = ide_object_get_context (IDE_OBJECT (build_system));
-  configmgr = ide_context_get_configuration_manager (context);
-  config = ide_configuration_manager_get_current (configmgr);
-
-  language = ide_file_get_language (file);
-
-  if (config == NULL || language == NULL)
-    goto failure;
-
-  id = gtk_source_language_get_id (language);
 
-  if (ide_str_equal0 (id, "c") || ide_str_equal0 (id, "chdr"))
-    env = ide_configuration_getenv (config, "CFLAGS");
-  else if (ide_str_equal0 (id, "cpp") || ide_str_equal0 (id, "cpphdr"))
-    env = ide_configuration_getenv (config, "CXXFLAGS");
-  else if (ide_str_equal0 (id, "vala"))
-    env = ide_configuration_getenv (config, "VALAFLAGS");
-
-  if (env != NULL)
-    {
-      gchar **flags = NULL;
-      gint argc;
-
-      if (g_shell_parse_argv (env, &argc, &flags, NULL))
-        {
-          g_task_return_pointer (task, flags, (GDestroyNotify)g_strfreev);
-          return;
-        }
-    }
-
-failure:
-  g_task_return_pointer (task, g_new0 (gchar*, 1), (GDestroyNotify)g_strfreev);
-}
-
-static gchar **
-ide_directory_build_system_get_build_flags_finish (IdeBuildSystem  *build_system,
-                                                   GAsyncResult    *result,
-                                                   GError         **error)
-{
-  GTask *task = (GTask *)result;
-
-  g_assert (IDE_IS_DIRECTORY_BUILD_SYSTEM (build_system));
-  g_assert (G_IS_TASK (task));
-
-  return g_task_propagate_pointer (task, error);
-}
-
-static gint
-ide_directory_build_system_get_priority (IdeBuildSystem *build_system)
-{
-  return 1000000;
+  return g_object_new (IDE_TYPE_SIMPLE_BUILDER,
+                       "configuration", configuration,
+                       "context", context,
+                       NULL);
 }
 
 static void
 build_system_init (IdeBuildSystemInterface *iface)
 {
-  iface->get_build_flags_async = ide_directory_build_system_get_build_flags_async;
-  iface->get_build_flags_finish = ide_directory_build_system_get_build_flags_finish;
   iface->get_priority = ide_directory_build_system_get_priority;
+  iface->get_builder = ide_directory_build_system_get_builder;
 }
diff --git a/libide/ide.h b/libide/ide.h
index fd397e6..78aa113 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -47,6 +47,7 @@ G_BEGIN_DECLS
 #include "buildsystem/ide-configuration.h"
 #include "buildsystem/ide-environment-variable.h"
 #include "buildsystem/ide-environment.h"
+#include "buildsystem/ide-simple-builder.h"
 #include "devices/ide-device-manager.h"
 #include "devices/ide-device-provider.h"
 #include "devices/ide-device.h"
diff --git a/libide/runner/ide-run-manager.c b/libide/runner/ide-run-manager.c
index 1792f9b..ade6557 100644
--- a/libide/runner/ide-run-manager.c
+++ b/libide/runner/ide-run-manager.c
@@ -721,22 +721,22 @@ ide_run_manager_discover_default_target_cb (GObject      *object,
                                             GAsyncResult *result,
                                             gpointer      user_data)
 {
-  IdeBuildSystem *build_system = (IdeBuildSystem *)object;
+  IdeBuilder *builder = (IdeBuilder *)object;
   g_autoptr(GTask) task = user_data;
   g_autoptr(GPtrArray) targets = NULL;
+  g_autoptr(GError) error = NULL;
   IdeBuildTarget *best_match;
-  GError *error = NULL;
 
   IDE_ENTRY;
 
-  g_assert (IDE_IS_BUILD_SYSTEM (build_system));
+  g_assert (IDE_IS_BUILDER (builder));
   g_assert (G_IS_ASYNC_RESULT (result));
 
-  targets = ide_build_system_get_build_targets_finish (build_system, result, &error);
+  targets = ide_builder_get_build_targets_finish (builder, result, &error);
 
   if (targets == NULL)
     {
-      g_task_return_error (task, error);
+      g_task_return_error (task, g_steal_pointer (&error));
       IDE_EXIT;
     }
 
@@ -763,6 +763,10 @@ ide_run_manager_discover_default_target_async (IdeRunManager       *self,
                                                gpointer             user_data)
 {
   g_autoptr(GTask) task = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(IdeBuilder) builder = NULL;
+  IdeConfigurationManager *config_manager;
+  IdeConfiguration *config;
   IdeBuildSystem *build_system;
   IdeContext *context;
 
@@ -776,11 +780,22 @@ ide_run_manager_discover_default_target_async (IdeRunManager       *self,
 
   context = ide_object_get_context (IDE_OBJECT (self));
   build_system = ide_context_get_build_system (context);
+  config_manager = ide_context_get_configuration_manager (context);
+
+  config = ide_configuration_manager_get_current (config_manager);
+
+  builder = ide_build_system_get_builder (build_system, config, &error);
+
+  if (builder == NULL)
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
 
-  ide_build_system_get_build_targets_async (build_system,
-                                            cancellable,
-                                            ide_run_manager_discover_default_target_cb,
-                                            g_object_ref (task));
+  ide_builder_get_build_targets_async (builder,
+                                       cancellable,
+                                       ide_run_manager_discover_default_target_cb,
+                                       g_steal_pointer (&task));
 
   IDE_EXIT;
 }
diff --git a/libide/subprocess/ide-subprocess-launcher.c b/libide/subprocess/ide-subprocess-launcher.c
index 7f04cf6..60eead3 100644
--- a/libide/subprocess/ide-subprocess-launcher.c
+++ b/libide/subprocess/ide-subprocess-launcher.c
@@ -828,7 +828,8 @@ ide_subprocess_launcher_insert_argv (IdeSubprocessLauncher *self,
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
-  g_return_if_fail (index < priv->argv->len);
+  g_return_if_fail (priv->argv->len > 0);
+  g_return_if_fail (index < (priv->argv->len - 1));
   g_return_if_fail (arg != NULL);
 
   g_ptr_array_insert (priv->argv, index, g_strdup (arg));
@@ -840,11 +841,15 @@ ide_subprocess_launcher_replace_argv (IdeSubprocessLauncher *self,
                                       const gchar           *arg)
 {
   IdeSubprocessLauncherPrivate *priv = ide_subprocess_launcher_get_instance_private (self);
+  gchar *old_arg;
 
   g_return_if_fail (IDE_IS_SUBPROCESS_LAUNCHER (self));
-  g_return_if_fail (index < priv->argv->len);
+  g_return_if_fail (priv->argv->len > 0);
+  g_return_if_fail (index < (priv->argv->len - 1));
   g_return_if_fail (arg != NULL);
 
-  g_ptr_array_remove_index (priv->argv, index);
-  g_ptr_array_insert (priv->argv, (index == priv->argv->len ? -1 : index), g_strdup (arg));
+  /* overwriting in place */
+  old_arg = g_ptr_array_index (priv->argv, index);
+  g_ptr_array_index (priv->argv, index) = g_strdup (arg);
+  g_free (old_arg);
 }
diff --git a/plugins/autotools/ide-autotools-build-system.c b/plugins/autotools/ide-autotools-build-system.c
index 044c11f..00f61b1 100644
--- a/plugins/autotools/ide-autotools-build-system.c
+++ b/plugins/autotools/ide-autotools-build-system.c
@@ -20,28 +20,20 @@
 
 #include "config.h"
 
-#include <egg-counter.h>
-#include <egg-task-cache.h>
-#include <glib/gi18n.h>
 #include <gio/gio.h>
 #include <gtksourceview/gtksource.h>
 #include <ide.h>
-#include <ide-internal.h>
 
 #include "ide-autotools-build-system.h"
 #include "ide-autotools-builder.h"
 #include "ide-makecache.h"
 
-#define MAKECACHE_KEY "makecache"
-#define DEFAULT_MAKECACHE_TTL 0
-
 struct _IdeAutotoolsBuildSystem
 {
-  IdeObject     parent_instance;
+  IdeObject  parent_instance;
 
-  GFile        *project_file;
-  EggTaskCache *task_cache;
-  gchar        *tarball_name;
+  GFile     *project_file;
+  gchar     *tarball_name;
 };
 
 static void async_initable_iface_init (GAsyncInitableIface *iface);
@@ -55,8 +47,6 @@ G_DEFINE_TYPE_WITH_CODE (IdeAutotoolsBuildSystem,
                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
                          G_IMPLEMENT_INTERFACE (IDE_TYPE_BUILD_SYSTEM, build_system_iface_init))
 
-EGG_DEFINE_COUNTER (build_flags, "Autotools", "Flags Requests", "Requests count for build flags")
-
 enum {
   PROP_0,
   PROP_PROJECT_FILE,
@@ -82,17 +72,20 @@ ide_autotools_build_system_get_builder (IdeBuildSystem    *build_system,
   IdeBuilder *ret;
   IdeContext *context;
 
+  IDE_ENTRY;
+
   g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (build_system));
   g_assert (IDE_IS_CONFIGURATION (configuration));
 
   context = ide_object_get_context (IDE_OBJECT (build_system));
+  g_assert (IDE_IS_CONTEXT (context));
 
   ret = g_object_new (IDE_TYPE_AUTOTOOLS_BUILDER,
                       "context", context,
                       "configuration", configuration,
                       NULL);
 
-  return ret;
+  IDE_RETURN (ret);
 }
 
 static gboolean
@@ -117,57 +110,45 @@ ide_autotools_build_system_discover_file_worker (GTask        *task,
                                                  gpointer      task_data,
                                                  GCancellable *cancellable)
 {
+  g_autoptr(GFile) parent = NULL;
+  g_autoptr(GFile) configure_ac = NULL;
+  g_autoptr(GFile) configure_in = NULL;
   GFile *file = task_data;
-  GFile *parent;
 
   g_assert (G_IS_TASK (task));
   g_assert (G_IS_FILE (file));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  /*
+   * Previously, we used to walk up the tree looking for configure.ac. But
+   * that causes more problems than it solves as it means that we cannot
+   * handle test projects inside the Builder project (and other projects
+   * may have the same issue for sub-projects).
+   */
+
   if (is_configure (file) && g_file_query_exists (file, cancellable))
     {
       g_task_return_pointer (task, g_object_ref (file), g_object_unref);
       return;
     }
 
-  parent = g_object_ref (file);
+  if (g_file_query_file_type (file, 0, cancellable) == G_FILE_TYPE_DIRECTORY)
+    parent = g_object_ref (file);
+  else
+    parent = g_file_get_parent (file);
 
-  while (parent != NULL)
-    {
-      GFile *child;
-      GFile *tmp;
-
-      child = g_file_get_child (parent, "configure.ac");
-      if (g_file_query_exists (child, cancellable))
-        {
-          g_task_return_pointer (task, g_object_ref (child), g_object_unref);
-          g_clear_object (&child);
-          g_clear_object (&parent);
-          return;
-        }
-
-      child = g_file_get_child (parent, "configure.in");
-      if (g_file_query_exists (child, cancellable))
-        {
-          g_task_return_pointer (task, g_object_ref (child), g_object_unref);
-          g_clear_object (&child);
-          g_clear_object (&parent);
-          return;
-        }
-
-      g_clear_object (&child);
-
-      tmp = parent;
-      parent = g_file_get_parent (parent);
-      g_clear_object (&tmp);
-    }
+  configure_ac = g_file_get_child (parent, "configure.ac");
+  if (g_file_query_exists (configure_ac, cancellable))
+    g_task_return_pointer (task, g_steal_pointer (&configure_ac), g_object_unref);
 
-  g_clear_object (&parent);
+  configure_in = g_file_get_child (parent, "configure.in");
+  if (g_file_query_exists (configure_in, cancellable))
+    g_task_return_pointer (task, g_steal_pointer (&configure_in), g_object_unref);
 
   g_task_return_new_error (task,
                            G_IO_ERROR,
                            G_IO_ERROR_NOT_FOUND,
-                           _("Failed to locate configure.ac"));
+                           "Failed to locate configure.ac");
 }
 
 static void
@@ -200,275 +181,7 @@ ide_autotools_build_system_discover_file_finish (IdeAutotoolsBuildSystem  *syste
   return g_task_propagate_pointer (task, error);
 }
 
-static void
-ide_autotools_build_system_get_local_makefile_async (IdeAutotoolsBuildSystem *self,
-                                                     GCancellable            *cancellable,
-                                                     GAsyncReadyCallback      callback,
-                                                     gpointer                 user_data)
-{
-  IdeContext *context;
-  g_autoptr(IdeConfiguration) configuration = NULL;
-  g_autoptr(GTask) task = NULL;
-  g_autoptr(IdeBuilder) builder = NULL;
-  g_autoptr(GFile) build_directory = NULL;
-  g_autoptr(GFile) makefile = NULL;
-  GError *error = NULL;
-
-  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  task = g_task_new (self, cancellable, callback, user_data);
-
-  context = ide_object_get_context (IDE_OBJECT (self));
-
-  configuration = ide_configuration_new (context, "autotools-bootstrap", "local", "host");
-
-  builder = ide_autotools_build_system_get_builder (IDE_BUILD_SYSTEM (self), configuration, &error);
-
-  if (builder == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  build_directory = ide_autotools_builder_get_build_directory (IDE_AUTOTOOLS_BUILDER (builder));
-  makefile = g_file_get_child (build_directory, "Makefile");
-
-  g_task_return_pointer (task, g_object_ref (makefile), g_object_unref);
-}
-
-static GFile *
-ide_autotools_build_system_get_local_makefile_finish (IdeAutotoolsBuildSystem  *self,
-                                                      GAsyncResult             *result,
-                                                      GError                  **error)
-{
-  GTask *task = (GTask *)result;
-
-  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_assert (G_IS_TASK (task));
-
-  return g_task_propagate_pointer (task, error);
-}
-
-static void
-populate_cache__new_makecache_cb (GObject      *object,
-                                  GAsyncResult *result,
-                                  gpointer      user_data)
-{
-  g_autoptr(GTask) task = user_data;
-  IdeMakecache *makecache;
-  GError *error = NULL;
-
-  g_assert (G_IS_TASK (task));
-
-  if (!(makecache = ide_makecache_new_for_makefile_finish (result, &error)))
-    g_task_return_error (task, error);
-  else
-    g_task_return_pointer (task, makecache, g_object_unref);
-}
-
-static void
-populate_cache__get_local_makefile_cb (GObject      *object,
-                                       GAsyncResult *result,
-                                       gpointer      user_data)
-{
-  IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)object;
-  g_autoptr(GTask) task = user_data;
-  g_autoptr(GFile) makefile = NULL;
-  IdeContext *context;
-  GError *error = NULL;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_assert (G_IS_TASK (task));
-
-  makefile = ide_autotools_build_system_get_local_makefile_finish (self, result, &error);
-
-  if (makefile == NULL)
-    {
-      g_task_return_error (task, error);
-      IDE_EXIT;
-    }
-
-  context = ide_object_get_context (IDE_OBJECT (self));
-  ide_makecache_new_for_makefile_async (context,
-                                        makefile,
-                                        g_task_get_cancellable (task),
-                                        populate_cache__new_makecache_cb,
-                                        g_object_ref (task));
-
-  IDE_EXIT;
-}
-
-static void
-populate_cache_cb (EggTaskCache  *cache,
-                   gconstpointer  key,
-                   GTask         *task,
-                   gpointer       user_data)
-{
-  IdeAutotoolsBuildSystem *self = user_data;
-
-  IDE_ENTRY;
-
-  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_assert (ide_str_equal0 (key, MAKECACHE_KEY));
-  g_assert (G_IS_TASK (task));
-
-  ide_autotools_build_system_get_local_makefile_async (self,
-                                                       g_task_get_cancellable (task),
-                                                       populate_cache__get_local_makefile_cb,
-                                                       g_object_ref (task));
-
-  IDE_EXIT;
-}
-
-static void
-ide_autotools_build_system_get_makecache_cb (GObject      *object,
-                                             GAsyncResult *result,
-                                             gpointer      user_data)
-{
-  EggTaskCache *task_cache = (EggTaskCache *)object;
-  g_autoptr(GTask) task = user_data;
-  IdeMakecache *ret;
-  GError *error = NULL;
-
-  if (!(ret = egg_task_cache_get_finish (task_cache, result, &error)))
-    g_task_return_error (task, error);
-  else
-    g_task_return_pointer (task, ret, g_object_unref);
-}
-
-static void
-ide_autotools_build_system_get_makecache_async (IdeAutotoolsBuildSystem *self,
-                                                GCancellable            *cancellable,
-                                                GAsyncReadyCallback      callback,
-                                                gpointer                 user_data)
-{
-  g_autoptr(GTask) task = NULL;
-
-  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  task = g_task_new (self, cancellable, callback, user_data);
-
-  egg_task_cache_get_async (self->task_cache,
-                            MAKECACHE_KEY,
-                            FALSE,
-                            cancellable,
-                            ide_autotools_build_system_get_makecache_cb,
-                            g_object_ref (task));
-}
-
-static IdeMakecache *
-ide_autotools_build_system_get_makecache_finish (IdeAutotoolsBuildSystem  *self,
-                                                 GAsyncResult             *result,
-                                                 GError                  **error)
-{
-  GTask *task = (GTask *)result;
-
-  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self), NULL);
-  g_return_val_if_fail (G_IS_TASK (task), NULL);
-
-  return g_task_propagate_pointer (task, error);
-}
-
-static void
-ide_autotools_build_system__get_file_flags_cb (GObject      *object,
-                                               GAsyncResult *result,
-                                               gpointer      user_data)
-{
-  IdeMakecache *makecache = (IdeMakecache *)object;
-  g_autoptr(GTask) task = user_data;
-  gchar **flags;
-  GError *error = NULL;
-
-  g_assert (IDE_IS_MAKECACHE (makecache));
-  g_assert (G_IS_TASK (task));
-
-  flags = ide_makecache_get_file_flags_finish (makecache, result, &error);
-
-  if (!flags)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  g_task_return_pointer (task, flags, (GDestroyNotify)g_strfreev);
-}
-
-static void
-ide_autotools_build_system__makecache_cb (GObject      *object,
-                                          GAsyncResult *result,
-                                          gpointer      user_data)
-{
-  IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)object;
-  g_autoptr(IdeMakecache) makecache = NULL;
-  g_autoptr(GTask) task = user_data;
-  GError *error = NULL;
-  GFile *file;
-
-  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_assert (G_IS_TASK (task));
-
-  makecache = ide_autotools_build_system_get_makecache_finish (self, result, &error);
-
-  if (!makecache)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  file = g_task_get_task_data (task);
-  g_assert (G_IS_FILE (file));
-
-  ide_makecache_get_file_flags_async (makecache,
-                                      file,
-                                      g_task_get_cancellable (task),
-                                      ide_autotools_build_system__get_file_flags_cb,
-                                      g_object_ref (task));
-}
-
-static void
-ide_autotools_build_system_get_build_flags_async (IdeBuildSystem      *build_system,
-                                                  IdeFile             *file,
-                                                  GCancellable        *cancellable,
-                                                  GAsyncReadyCallback  callback,
-                                                  gpointer             user_data)
-{
-  IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)build_system;
-  g_autoptr(GTask) task = NULL;
-  GFile *gfile;
-
-  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_assert (IDE_IS_FILE (file));
-
-  EGG_COUNTER_INC (build_flags);
-
-  gfile = ide_file_get_file (file);
-
-  task = g_task_new (self, cancellable, callback, user_data);
-  g_task_set_task_data (task, g_object_ref (gfile), g_object_unref);
-
-  ide_autotools_build_system_get_makecache_async (self,
-                                                  cancellable,
-                                                  ide_autotools_build_system__makecache_cb,
-                                                  g_object_ref (task));
-}
-
-static gchar **
-ide_autotools_build_system_get_build_flags_finish (IdeBuildSystem  *build_system,
-                                                   GAsyncResult    *result,
-                                                   GError         **error)
-{
-  GTask *task = (GTask *)result;
-
-  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (build_system));
-  g_assert (G_IS_TASK (task));
-
-  return g_task_propagate_pointer (task, error);
-}
-
+#if 0
 static gboolean
 looks_like_makefile (IdeBuffer *buffer)
 {
@@ -501,6 +214,7 @@ looks_like_makefile (IdeBuffer *buffer)
 
   return FALSE;
 }
+#endif
 
 static void
 ide_autotools_build_system__buffer_saved_cb (IdeAutotoolsBuildSystem *self,
@@ -511,19 +225,10 @@ ide_autotools_build_system__buffer_saved_cb (IdeAutotoolsBuildSystem *self,
   g_assert (IDE_IS_BUFFER (buffer));
   g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
 
+#if 0
   if (looks_like_makefile (buffer))
     egg_task_cache_evict (self->task_cache, MAKECACHE_KEY);
-}
-
-static void
-ide_autotools_build_system__config_changed_cb (IdeAutotoolsBuildSystem *self,
-                                               GParamSpec              *pspec,
-                                               IdeConfigurationManager *config_manager)
-{
-  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_assert (IDE_IS_CONFIGURATION_MANAGER (config_manager));
-
-  egg_task_cache_evict (self->task_cache, MAKECACHE_KEY);
+#endif
 }
 
 static void
@@ -537,7 +242,9 @@ ide_autotools_build_system__vcs_changed_cb (IdeAutotoolsBuildSystem *self,
 
   IDE_TRACE_MSG ("VCS has changed, evicting cached makecaches");
 
+#if 0
   egg_task_cache_evict_all (self->task_cache);
+#endif
 
   IDE_EXIT;
 }
@@ -568,15 +275,16 @@ static void
 ide_autotools_build_system_constructed (GObject *object)
 {
   IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)object;
-  IdeConfigurationManager *config_manager;
   IdeBufferManager *buffer_manager;
   IdeContext *context;
 
   G_OBJECT_CLASS (ide_autotools_build_system_parent_class)->constructed (object);
 
   context = ide_object_get_context (IDE_OBJECT (self));
+  g_assert (IDE_IS_CONTEXT (context));
+
   buffer_manager = ide_context_get_buffer_manager (context);
-  config_manager = ide_context_get_configuration_manager (context);
+  g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
 
   g_signal_connect_object (context,
                            "loaded",
@@ -585,16 +293,6 @@ ide_autotools_build_system_constructed (GObject *object)
                            G_CONNECT_SWAPPED);
 
   /*
-   * Track change of active configuration so that we invalidate our cached build
-   * targets (which might be out of date).
-   */
-  g_signal_connect_object (config_manager,
-                           "notify::current",
-                           G_CALLBACK (ide_autotools_build_system__config_changed_cb),
-                           self,
-                           G_CONNECT_SWAPPED);
-
-  /*
    * FIXME:
    *
    * We could setup and try to track all of the makefiles in the system
@@ -612,113 +310,6 @@ ide_autotools_build_system_constructed (GObject *object)
                            G_CONNECT_SWAPPED);
 }
 
-static void
-ide_autotools_build_system_get_build_targets_cb2 (GObject      *object,
-                                                  GAsyncResult *result,
-                                                  gpointer      user_data)
-{
-  IdeMakecache *makecache = (IdeMakecache *)object;
-  g_autoptr(GTask) task = user_data;
-  GPtrArray *ret;
-  GError *error = NULL;
-
-  g_assert (IDE_IS_MAKECACHE (makecache));
-  g_assert (G_IS_TASK (task));
-
-  ret = ide_makecache_get_build_targets_finish (makecache, result, &error);
-
-  if (ret == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  g_task_return_pointer (task, ret, (GDestroyNotify)g_ptr_array_unref);
-}
-
-static void
-ide_autotools_build_system_get_build_targets_cb (GObject      *object,
-                                                 GAsyncResult *result,
-                                                 gpointer      user_data)
-{
-  IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)object;
-  IdeContext *context;
-  IdeVcs *vcs;
-  g_autoptr(IdeConfiguration) configuration = NULL;
-  g_autoptr(IdeBuilder) builder = NULL;
-  g_autoptr(GFile) build_dir = NULL;
-  g_autoptr(IdeMakecache) makecache = NULL;
-  g_autoptr(GTask) task = user_data;
-  GError *error = NULL;
-
-  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_assert (G_IS_TASK (task));
-
-  makecache = ide_autotools_build_system_get_makecache_finish (self, result, &error);
-
-  if (makecache == NULL)
-    {
-      g_task_return_error (task, error);
-      return;
-    }
-
-  context = ide_object_get_context (IDE_OBJECT (self));
-  configuration = ide_configuration_new (context, "autotools-bootstrap", "local", "host");
-  builder = ide_autotools_build_system_get_builder (IDE_BUILD_SYSTEM (self), configuration, &error);
-  if (builder)
-    {
-      build_dir = ide_autotools_builder_get_build_directory (IDE_AUTOTOOLS_BUILDER (builder));
-    }
-  else
-    {
-      vcs = ide_context_get_vcs (context);
-      build_dir = ide_vcs_get_working_directory (vcs);
-    }
-
-  ide_makecache_get_build_targets_async (makecache,
-                                         build_dir,
-                                         g_task_get_cancellable (task),
-                                         ide_autotools_build_system_get_build_targets_cb2,
-                                         g_object_ref (task));
-}
-
-static void
-ide_autotools_build_system_get_build_targets_async (IdeBuildSystem      *build_system,
-                                                    GCancellable        *cancellable,
-                                                    GAsyncReadyCallback  callback,
-                                                    gpointer             user_data)
-{
-  IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)build_system;
-  g_autoptr(GTask) task = NULL;
-
-  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  task = g_task_new (self, cancellable, callback, user_data);
-  g_task_set_source_tag (task, ide_autotools_build_system_get_build_targets_async);
-
-  ide_autotools_build_system_get_makecache_async (self,
-                                                  cancellable,
-                                                  ide_autotools_build_system_get_build_targets_cb,
-                                                  g_object_ref (task));
-}
-
-static GPtrArray *
-ide_autotools_build_system_get_build_targets_finish (IdeBuildSystem  *build_system,
-                                                     GAsyncResult    *result,
-                                                     GError         **error)
-{
-  IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)build_system;
-  GTask *task = (GTask *)result;
-
-  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
-  g_assert (G_IS_TASK (task));
-  g_assert (g_task_is_valid (task, self));
-  g_assert (g_task_get_source_tag (task) == ide_autotools_build_system_get_build_targets_async);
-
-  return g_task_propagate_pointer (task, error);
-}
-
 static gint
 ide_autotools_build_system_get_priority (IdeBuildSystem *system)
 {
@@ -731,7 +322,7 @@ ide_autotools_build_system_finalize (GObject *object)
   IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)object;
 
   g_clear_pointer (&self->tarball_name, g_free);
-  g_clear_object (&self->task_cache);
+  g_clear_object (&self->project_file);
 
   G_OBJECT_CLASS (ide_autotools_build_system_parent_class)->finalize (object);
 }
@@ -785,10 +376,6 @@ build_system_iface_init (IdeBuildSystemInterface *iface)
 {
   iface->get_priority = ide_autotools_build_system_get_priority;
   iface->get_builder = ide_autotools_build_system_get_builder;
-  iface->get_build_flags_async = ide_autotools_build_system_get_build_flags_async;
-  iface->get_build_flags_finish = ide_autotools_build_system_get_build_flags_finish;
-  iface->get_build_targets_async = ide_autotools_build_system_get_build_targets_async;
-  iface->get_build_targets_finish = ide_autotools_build_system_get_build_targets_finish;
 }
 
 static void
@@ -821,27 +408,6 @@ ide_autotools_build_system_class_init (IdeAutotoolsBuildSystemClass *klass)
 static void
 ide_autotools_build_system_init (IdeAutotoolsBuildSystem *self)
 {
-  /*
-   * We actually only use this task cache for one instance, but it really
-   * makes it convenient to cache the result of even a single item so we
-   * can avoid async races in replies, as well as avoiding duplicate work.
-   *
-   * We don't require a ref/unref for the populate callback data since we
-   * will always have a GTask queued holding a reference during the lifetime
-   * of the populate callback execution.
-   */
-  self->task_cache = egg_task_cache_new (g_str_hash,
-                                         g_str_equal,
-                                         (GBoxedCopyFunc)g_strdup,
-                                         g_free,
-                                         g_object_ref,
-                                         g_object_unref,
-                                         DEFAULT_MAKECACHE_TTL,
-                                         populate_cache_cb,
-                                         self,
-                                         NULL);
-
-  egg_task_cache_set_name (self->task_cache, "makecache");
 }
 
 static void
diff --git a/plugins/autotools/ide-autotools-build-task.c b/plugins/autotools/ide-autotools-build-task.c
index 390a325..ca30ef6 100644
--- a/plugins/autotools/ide-autotools-build-task.c
+++ b/plugins/autotools/ide-autotools-build-task.c
@@ -56,6 +56,7 @@ typedef struct
   IdeRuntime            *runtime;
   IdeBuildCommandQueue  *postbuild;
   IdeEnvironment        *environment;
+  IdeBuilderBuildFlags   flags;
   guint                  sequence;
   guint                  require_autogen : 1;
   guint                  require_configure : 1;
@@ -488,6 +489,7 @@ worker_state_new (IdeAutotoolsBuildTask  *self,
     project_dir = g_object_ref (project_file);
 
   state = g_slice_new0 (WorkerState);
+  state->flags = flags;
   state->sequence = ide_configuration_get_sequence (self->configuration);
   state->require_autogen = self->require_autogen || FLAG_SET (flags, 
IDE_BUILDER_BUILD_FLAGS_FORCE_BOOTSTRAP);
   state->require_configure = self->require_configure || (state->require_autogen && FLAG_UNSET (flags, 
IDE_BUILDER_BUILD_FLAGS_NO_CONFIGURE));
@@ -805,6 +807,7 @@ ide_autotools_build_task_execute_with_postbuild_cb (GObject      *object,
   g_autoptr(GError) error = NULL;
   IdeRuntime *runtime;
   GCancellable *cancellable;
+  IdeBuilderBuildFlags flags;
 
   g_assert (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
   g_assert (G_IS_ASYNC_RESULT (result));
@@ -828,20 +831,27 @@ ide_autotools_build_task_execute_with_postbuild_cb (GObject      *object,
       return;
     }
 
+  flags = GPOINTER_TO_UINT (g_task_get_task_data (task));
   cancellable = g_task_get_cancellable (task);
 
-  if (self->install)
-    ide_runtime_postinstall_async (runtime,
-                                   IDE_BUILD_RESULT (self),
-                                   cancellable,
-                                   ide_autotools_build_task_postbuild_runtime_cb,
-                                   g_steal_pointer (&task));
-  else
-    ide_runtime_postbuild_async (runtime,
-                                 IDE_BUILD_RESULT (self),
-                                 cancellable,
-                                 ide_autotools_build_task_postbuild_runtime_cb,
-                                 g_steal_pointer (&task));
+  if ((flags & IDE_BUILDER_BUILD_FLAGS_NO_BUILD) == 0)
+    {
+      if (self->install)
+        ide_runtime_postinstall_async (runtime,
+                                       IDE_BUILD_RESULT (self),
+                                       cancellable,
+                                       ide_autotools_build_task_postbuild_runtime_cb,
+                                       g_steal_pointer (&task));
+      else
+        ide_runtime_postbuild_async (runtime,
+                                     IDE_BUILD_RESULT (self),
+                                     cancellable,
+                                     ide_autotools_build_task_postbuild_runtime_cb,
+                                     g_steal_pointer (&task));
+      return;
+    }
+
+  g_task_return_boolean (task, TRUE);
 }
 
 void
@@ -858,6 +868,7 @@ ide_autotools_build_task_execute_with_postbuild (IdeAutotoolsBuildTask *self,
 
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, ide_autotools_build_task_execute_with_postbuild);
+  g_task_set_task_data (task, GUINT_TO_POINTER (flags), NULL);
 
   ide_autotools_build_task_execute_async (self,
                                           flags,
@@ -1144,24 +1155,25 @@ step_configure (GTask                 *task,
 }
 
 static gboolean
-step_make_all  (GTask                 *task,
-                IdeAutotoolsBuildTask *self,
-                WorkerState           *state,
-                GCancellable          *cancellable)
+step_make_all (GTask                 *task,
+               IdeAutotoolsBuildTask *self,
+               WorkerState           *state,
+               GCancellable          *cancellable)
 {
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
   g_autoptr(IdeSubprocess) process = NULL;
-  const gchar * const *targets;
   const gchar *make = NULL;
-  gchar *default_targets[] = { "all", NULL };
   GError *error = NULL;
-  guint i;
 
   g_assert (G_IS_TASK (task));
   g_assert (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
-  g_assert (state);
+  g_assert (state != NULL);
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  if (g_strv_length (state->make_targets) == 0 ||
+      (state->flags & IDE_BUILDER_BUILD_FLAGS_NO_BUILD) != 0)
+    return TRUE;
+
   if (NULL == (launcher = ide_runtime_create_launcher (state->runtime, &error)))
     {
       g_task_return_error (task, error);
@@ -1193,14 +1205,9 @@ step_make_all  (GTask                 *task,
       return FALSE;
     }
 
-  if (!g_strv_length (state->make_targets))
-    targets = (const gchar * const *)default_targets;
-  else
-    targets = (const gchar * const *)state->make_targets;
-
-  for (i = 0; targets [i]; i++)
+  for (guint i = 0; state->make_targets[i] != NULL; i++)
     {
-      const gchar *target = targets [i];
+      const gchar *target = state->make_targets [i];
 
       if (ide_str_equal0 (target, "clean"))
         ide_build_result_set_mode (IDE_BUILD_RESULT (self), _("Cleaningā€¦"));
diff --git a/plugins/autotools/ide-autotools-builder.c b/plugins/autotools/ide-autotools-builder.c
index e340311..61c1c63 100644
--- a/plugins/autotools/ide-autotools-builder.c
+++ b/plugins/autotools/ide-autotools-builder.c
@@ -16,11 +16,20 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#define G_LOG_DOMAIN "ide-autotools-builder"
+
+#include <egg-counter.h>
+#include <egg-task-cache.h>
 #include <glib/gi18n.h>
 #include <ide.h>
 
 #include "ide-autotools-build-task.h"
 #include "ide-autotools-builder.h"
+#include "ide-makecache.h"
+
+#define DEFAULT_MAKECACHE_TTL_MSECS (5 * 60 * 1000L)
+
+EGG_DEFINE_COUNTER (build_flags, "Autotools", "Flags Requests", "Requests count for build flags")
 
 struct _IdeAutotoolsBuilder
 {
@@ -29,6 +38,185 @@ struct _IdeAutotoolsBuilder
 
 G_DEFINE_TYPE (IdeAutotoolsBuilder, ide_autotools_builder, IDE_TYPE_BUILDER)
 
+static EggTaskCache *makecaches;
+
+static void
+get_makecache_cb (GObject      *object,
+                  GAsyncResult *result,
+                  gpointer      user_data)
+{
+  EggTaskCache *cache = (EggTaskCache *)object;
+  g_autoptr(IdeMakecache) makecache = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+
+  IDE_ENTRY;
+
+  g_assert (EGG_IS_TASK_CACHE (cache));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  makecache = egg_task_cache_get_finish (cache, result, &error);
+
+  if (makecache == NULL)
+    {
+      g_task_return_error (G_TASK (task), g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  g_task_return_pointer (task, g_steal_pointer (&makecache), g_object_unref);
+
+  IDE_EXIT;
+}
+
+static void
+get_makecache_async (IdeConfiguration    *configuration,
+                     GCancellable        *cancellable,
+                     GAsyncReadyCallback  callback,
+                     gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (NULL, cancellable, callback, user_data);
+  g_task_set_source_tag (task, get_makecache_async);
+
+  egg_task_cache_get_async (makecaches,
+                            configuration,
+                            FALSE,
+                            cancellable,
+                            get_makecache_cb,
+                            g_steal_pointer (&task));
+}
+
+static IdeMakecache *
+get_makecache_finish (GAsyncResult  *result,
+                      GError       **error)
+{
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_autotools_builder_get_build_flags_flags_cb (GObject      *object,
+                                                GAsyncResult *result,
+                                                gpointer      user_data)
+{
+  IdeMakecache *makecache = (IdeMakecache *)object;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+  g_auto(GStrv) flags = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAKECACHE (makecache));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  EGG_COUNTER_DEC (build_flags);
+
+  flags = ide_makecache_get_file_flags_finish (makecache, result, &error);
+
+  if (flags == NULL)
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  g_task_return_pointer (task, g_steal_pointer (&flags), (GDestroyNotify)g_strfreev);
+
+  IDE_EXIT;
+}
+
+static void
+ide_autotools_builder_get_build_flags_makecache_cb (GObject      *object,
+                                                    GAsyncResult *result,
+                                                    gpointer      user_data)
+{
+  g_autoptr(IdeMakecache) makecache = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+  GCancellable *cancellable;
+  GFile *file;
+
+  IDE_ENTRY;
+
+  g_assert (object == NULL);
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  makecache = get_makecache_finish (result, &error);
+
+  if (makecache == NULL)
+    {
+      EGG_COUNTER_DEC (build_flags);
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  cancellable = g_task_get_cancellable (task);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  file = g_task_get_task_data (task);
+  g_assert (!file || G_IS_FILE (file));
+
+  ide_makecache_get_file_flags_async (makecache,
+                                      file,
+                                      cancellable,
+                                      ide_autotools_builder_get_build_flags_flags_cb,
+                                      g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static void
+ide_autotools_builder_get_build_flags_async (IdeBuilder          *builder,
+                                             IdeFile             *file,
+                                             GCancellable        *cancellable,
+                                             GAsyncReadyCallback  callback,
+                                             gpointer             user_data)
+{
+  IdeAutotoolsBuilder *self = (IdeAutotoolsBuilder *)builder;
+  g_autoptr(GTask) task = NULL;
+  IdeConfiguration *configuration;
+  GFile *gfile;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (self));
+  g_assert (IDE_IS_FILE (file));
+
+  EGG_COUNTER_INC (build_flags);
+
+  gfile = ide_file_get_file (file);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_autotools_builder_get_build_flags_async);
+  g_task_set_task_data (task, g_object_ref (gfile), g_object_unref);
+
+  configuration = ide_builder_get_configuration (builder);
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+
+  get_makecache_async (configuration,
+                       cancellable,
+                       ide_autotools_builder_get_build_flags_makecache_cb,
+                       g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static gchar **
+ide_autotools_builder_get_build_flags_finish (IdeBuilder    *builder,
+                                              GAsyncResult  *result,
+                                              GError       **error)
+{
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (builder));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+
 static void
 ide_autotools_builder_build_cb (GObject      *object,
                                 GAsyncResult *result,
@@ -38,8 +226,8 @@ ide_autotools_builder_build_cb (GObject      *object,
   IdeAutotoolsBuildTask *build_result = (IdeAutotoolsBuildTask *)object;
   GError *error = NULL;
 
-  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (build_result));
-  g_return_if_fail (G_IS_TASK (task));
+  g_assert (IDE_IS_AUTOTOOLS_BUILD_TASK (build_result));
+  g_assert (G_IS_TASK (task));
 
   if (!ide_autotools_build_task_execute_with_postbuild_finish (build_result, result, &error))
     {
@@ -141,8 +329,8 @@ ide_autotools_builder_build_async (IdeBuilder           *builder,
   IdeConfiguration *configuration;
   IdeContext *context;
 
-  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder));
-  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILDER (self));
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (builder));
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (self));
 
   if (ide_autotools_builder_get_needs_bootstrap (self))
     flags |= IDE_BUILDER_BUILD_FLAGS_FORCE_BOOTSTRAP;
@@ -178,13 +366,118 @@ ide_autotools_builder_build_finish (IdeBuilder    *builder,
 {
   GTask *task = (GTask *)result;
 
-  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder), NULL);
-  g_return_val_if_fail (G_IS_TASK (task), NULL);
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (builder));
+  g_assert (G_IS_TASK (task));
 
   return g_task_propagate_pointer (task, error);
 }
 
 static void
+ide_autotools_builder_get_build_targets_targets_cb (GObject      *object,
+                                                    GAsyncResult *result,
+                                                    gpointer      user_data)
+{
+  IdeMakecache *makecache = (IdeMakecache *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GPtrArray) ret = NULL;
+
+  g_assert (IDE_IS_MAKECACHE (makecache));
+  g_assert (G_IS_TASK (task));
+
+  ret = ide_makecache_get_build_targets_finish (makecache, result, &error);
+
+  if (ret == NULL)
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_pointer (task, g_steal_pointer (&ret), (GDestroyNotify)g_ptr_array_unref);
+
+  IDE_EXIT;
+}
+
+static void
+ide_autotools_builder_get_build_targets_makecache_cb (GObject      *object,
+                                                      GAsyncResult *result,
+                                                      gpointer      user_data)
+{
+  g_autoptr(IdeMakecache) makecache = NULL;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  GCancellable *cancellable;
+  GFile *build_dir;
+
+  IDE_ENTRY;
+
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  makecache = get_makecache_finish (result, &error);
+
+  if (makecache == NULL)
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  build_dir = g_task_get_task_data (task);
+  g_assert (G_IS_FILE (build_dir));
+
+  cancellable = g_task_get_cancellable (task);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  ide_makecache_get_build_targets_async (makecache,
+                                         build_dir,
+                                         cancellable,
+                                         ide_autotools_builder_get_build_targets_targets_cb,
+                                         g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static void
+ide_autotools_builder_get_build_targets_async (IdeBuilder          *builder,
+                                               GCancellable        *cancellable,
+                                               GAsyncReadyCallback  callback,
+                                               gpointer             user_data)
+{
+  IdeAutotoolsBuilder *self = (IdeAutotoolsBuilder *)builder;
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GFile) build_dir = NULL;
+  IdeConfiguration *configuration;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  build_dir = ide_autotools_builder_get_build_directory (self);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_autotools_builder_get_build_targets_async);
+  g_task_set_task_data (task, g_steal_pointer (&build_dir), g_object_unref);
+
+  configuration = ide_builder_get_configuration (builder);
+
+  get_makecache_async (configuration,
+                       cancellable,
+                       ide_autotools_builder_get_build_targets_makecache_cb,
+                       g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static GPtrArray *
+ide_autotools_builder_get_build_targets_finish (IdeBuilder    *builder,
+                                                GAsyncResult  *result,
+                                                GError       **error)
+{
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (builder));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
 ide_autotools_builder_install_cb (GObject      *object,
                                   GAsyncResult *result,
                                   gpointer      user_data)
@@ -227,8 +520,8 @@ ide_autotools_builder_install_async (IdeBuilder           *builder,
   IdeContext *context;
   IdeBuilderBuildFlags flags;
 
-  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder));
-  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILDER (self));
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (builder));
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (self));
 
   flags = IDE_BUILDER_BUILD_FLAGS_NONE;
   if (ide_autotools_builder_get_needs_bootstrap (self))
@@ -267,13 +560,172 @@ ide_autotools_builder_install_finish (IdeBuilder    *builder,
 {
   GTask *task = (GTask *)result;
 
-  g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder), FALSE);
-  g_return_val_if_fail (G_IS_TASK (task), FALSE);
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (builder));
+  g_assert (G_IS_TASK (task));
 
   return g_task_propagate_pointer (task, error);
 }
 
 static void
+new_makecache_cb (GObject      *object,
+                  GAsyncResult *result,
+                  gpointer      user_data)
+{
+  g_autoptr(IdeMakecache) makecache = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+
+  IDE_ENTRY;
+
+  makecache = ide_makecache_new_for_makefile_finish (result, &error);
+
+  if (makecache == NULL)
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_pointer (task, g_steal_pointer (&makecache), g_object_unref);
+
+  IDE_EXIT;
+}
+
+static void
+ensure_makefile_cb (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  IdeAutotoolsBuilder *builder = (IdeAutotoolsBuilder *)object;
+  g_autoptr(IdeBuildResult) build_resualt = NULL;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) build_directory = NULL;
+  g_autoptr(GFile) makefile = NULL;
+  g_autoptr(IdeBuildResult) build_result = NULL;
+  IdeConfiguration *configuration;
+  GCancellable *cancellable;
+  IdeRuntime *runtime;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_AUTOTOOLS_BUILDER (builder));
+  g_assert (G_IS_TASK (task));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  build_result = ide_builder_build_finish (IDE_BUILDER (builder), result, &error);
+
+  if (build_result == NULL)
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  configuration = ide_builder_get_configuration (IDE_BUILDER (builder));
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+
+  build_directory = ide_autotools_builder_get_build_directory (builder);
+  g_assert (G_IS_FILE (build_directory));
+
+  makefile = g_file_get_child (build_directory, "Makefile");
+  g_assert (G_IS_FILE (makefile));
+
+  runtime = ide_configuration_get_runtime (configuration);
+  g_assert (!runtime || IDE_IS_RUNTIME (runtime));
+
+  if (runtime == NULL)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               "Failed to locate runtime ā€œ%sā€",
+                               ide_configuration_get_runtime_id (configuration));
+      IDE_EXIT;
+    }
+
+  cancellable = g_task_get_cancellable (task);
+
+  ide_makecache_new_for_makefile_async (runtime,
+                                        makefile,
+                                        cancellable,
+                                        new_makecache_cb,
+                                        g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static void
+populate_cache_cb (EggTaskCache  *cache,
+                   gconstpointer  key,
+                   GTask         *task,
+                   gpointer       user_data)
+{
+  IdeConfiguration *configuration = (IdeConfiguration *)key;
+  g_autoptr(IdeBuilder) builder = NULL;
+  GCancellable *cancellable;
+  IdeContext *context;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+  g_assert (G_IS_TASK (task));
+
+  context = ide_object_get_context (IDE_OBJECT (configuration));
+  g_assert (IDE_IS_CONTEXT (context));
+
+  builder = g_object_new (IDE_TYPE_AUTOTOOLS_BUILDER,
+                          "context", context,
+                          "configuration", configuration,
+                          NULL);
+
+  cancellable = g_task_get_cancellable (task);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  /*
+   * Next we need to ensure that the project is at least boostrapped,
+   * so that we can do a dry run on the makefile. So we just use an
+   * additional IdeBuilder with an identical configuration so that
+   * we have access to the Makefile/etc.
+   *
+   * Once that is complete, we can locate the build Makefile and then
+   * run our make process (via makecache) in the target runtime.
+   */
+  ide_builder_build_async (builder,
+                           IDE_BUILDER_BUILD_FLAGS_NO_BUILD,
+                           NULL,
+                           cancellable,
+                           ensure_makefile_cb,
+                           g_object_ref (task));
+
+  IDE_EXIT;
+}
+
+static guint
+config_hash (gconstpointer a)
+{
+  g_autofree gchar *key = NULL;
+  IdeConfiguration *config = (IdeConfiguration *)a;
+
+  g_assert (IDE_IS_CONFIGURATION (config));
+
+  key = g_strdup_printf ("%s|%u",
+                         ide_configuration_get_id (config),
+                         ide_configuration_get_sequence (config));
+
+  return g_str_hash (key);
+}
+
+static gboolean
+config_equal (gconstpointer a,
+              gconstpointer b)
+{
+  IdeConfiguration *config_a = (IdeConfiguration *)a;
+  IdeConfiguration *config_b = (IdeConfiguration *)b;
+
+  g_assert (IDE_IS_CONFIGURATION (config_a));
+  g_assert (IDE_IS_CONFIGURATION (config_b));
+
+  return (g_strcmp0 (ide_configuration_get_id (config_a), ide_configuration_get_id (config_b)) == 0) &&
+         (ide_configuration_get_sequence (config_a) == ide_configuration_get_sequence (config_b));
+}
+
+static void
 ide_autotools_builder_class_init (IdeAutotoolsBuilderClass *klass)
 {
   IdeBuilderClass *builder_class = IDE_BUILDER_CLASS (klass);
@@ -282,6 +734,22 @@ ide_autotools_builder_class_init (IdeAutotoolsBuilderClass *klass)
   builder_class->build_finish = ide_autotools_builder_build_finish;
   builder_class->install_async = ide_autotools_builder_install_async;
   builder_class->install_finish = ide_autotools_builder_install_finish;
+  builder_class->get_build_flags_async = ide_autotools_builder_get_build_flags_async;
+  builder_class->get_build_flags_finish = ide_autotools_builder_get_build_flags_finish;
+  builder_class->get_build_targets_async = ide_autotools_builder_get_build_targets_async;
+  builder_class->get_build_targets_finish = ide_autotools_builder_get_build_targets_finish;
+
+  makecaches = egg_task_cache_new (config_hash,
+                                   config_equal,
+                                   g_object_ref,
+                                   g_object_unref,
+                                   g_object_ref,
+                                   g_object_unref,
+                                   DEFAULT_MAKECACHE_TTL_MSECS,
+                                   populate_cache_cb,
+                                   NULL,
+                                   NULL);
+  egg_task_cache_set_name (makecaches, "makecache");
 }
 
 static void
@@ -301,16 +769,26 @@ ide_autotools_builder_get_needs_bootstrap (IdeAutotoolsBuilder *self)
   g_return_val_if_fail (IDE_IS_AUTOTOOLS_BUILDER (self), FALSE);
 
   context = ide_object_get_context (IDE_OBJECT (self));
+  g_assert (IDE_IS_CONTEXT (context));
+
   vcs = ide_context_get_vcs (context);
   working_directory = ide_vcs_get_working_directory (vcs);
   configure = g_file_get_child (working_directory, "configure");
 
   if (!g_file_query_exists (configure, NULL))
-    return TRUE;
+    {
+      g_autofree gchar *path = g_file_get_path (configure);
+      IDE_TRACE_MSG ("project needs bootstrap because %s does not exist", path);
+      return TRUE;
+    }
 
   configuration = ide_builder_get_configuration (IDE_BUILDER (self));
+
   if (ide_configuration_get_dirty (configuration))
-    return TRUE;
+    {
+      IDE_TRACE_MSG ("project needs bootstrap because configuration is dirty");
+      return TRUE;
+    }
 
   /*
    * TODO:
diff --git a/plugins/autotools/ide-makecache.c b/plugins/autotools/ide-makecache.c
index afae0f6..14998cf 100644
--- a/plugins/autotools/ide-makecache.c
+++ b/plugins/autotools/ide-makecache.c
@@ -54,6 +54,7 @@ struct _IdeMakecache
   EggTaskCache *file_targets_cache;
   EggTaskCache *file_flags_cache;
   GPtrArray    *build_targets;
+  IdeRuntime   *runtime;
 };
 
 typedef struct
@@ -414,6 +415,7 @@ ide_makecache_new_worker (GTask        *task,
                           GCancellable *cancellable)
 {
   IdeMakecache *self = source_object;
+  IdeRuntime *runtime = task_data;
   IdeContext *context;
   IdeProject *project;
   const gchar *project_id;
@@ -423,10 +425,9 @@ ide_makecache_new_worker (GTask        *task,
   g_autoptr(GFile) parent = NULL;
   g_autofree gchar *workdir = NULL;
   g_autoptr(GMappedFile) mapped = NULL;
-  g_autoptr(GSubprocessLauncher) launcher = NULL;
-  g_autoptr(GSubprocess) subprocess = NULL;
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
   GError *error = NULL;
-  GPtrArray *args;
   int fdcopy;
   int fd;
 
@@ -434,6 +435,7 @@ ide_makecache_new_worker (GTask        *task,
 
   g_assert (G_IS_TASK (task));
   g_assert (IDE_IS_MAKECACHE (self));
+  g_assert (IDE_IS_RUNTIME (runtime));
 
   if (!self->makefile || !(parent = g_file_get_parent (self->makefile)))
     {
@@ -501,9 +503,7 @@ ide_makecache_new_worker (GTask        *task,
   /*
    * Step 2, make an extra fd to be passed to the child process.
    */
-  fdcopy = dup (fd);
-
-  if (fdcopy == -1)
+  if (-1 == (fdcopy = dup (fd)))
     {
       g_task_return_new_error (task,
                                G_IO_ERROR,
@@ -519,32 +519,29 @@ ide_makecache_new_worker (GTask        *task,
    *
    * Spawn `make -p -n -s` in the directory containing our makefile.
    */
-  launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
-  args = g_ptr_array_new ();
-  g_ptr_array_add (args, GNU_MAKE_NAME);
-  g_ptr_array_add (args, "-p");
-  g_ptr_array_add (args, "-n");
-  g_ptr_array_add (args, "-s");
-  g_ptr_array_add (args, NULL);
-  g_subprocess_launcher_set_cwd (launcher, workdir);
-  g_subprocess_launcher_take_stdout_fd (launcher, fdcopy);
+  launcher = ide_runtime_create_launcher (runtime, &error);
 
-#ifdef IDE_ENABLE_TRACE
-  {
-    g_autofree gchar *str = NULL;
-    str = g_strjoinv (" ", (gchar **)args->pdata);
-    IDE_TRACE_MSG ("workdir=%s Launching '%s'", workdir, str);
-  }
-#endif
+  if (launcher == NULL)
+    {
+      g_task_return_error (task, error);
+      close (fdcopy);
+      close (fd);
+      IDE_EXIT;
+    }
 
-  subprocess = g_subprocess_launcher_spawnv (launcher,
-                                             (const gchar * const *)args->pdata,
-                                             &error);
+  ide_subprocess_launcher_push_argv (launcher, GNU_MAKE_NAME);
+  ide_subprocess_launcher_push_argv (launcher, "-p");
+  ide_subprocess_launcher_push_argv (launcher, "-n");
+  ide_subprocess_launcher_push_argv (launcher, "-s");
+  ide_subprocess_launcher_set_cwd (launcher, workdir);
 
-  g_ptr_array_free (args, TRUE);
+  ide_subprocess_launcher_set_flags (launcher, G_SUBPROCESS_FLAGS_STDERR_SILENCE);
+  ide_subprocess_launcher_take_stdout_fd (launcher, fdcopy);
   fdcopy = -1;
 
-  if (!subprocess)
+  subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, &error);
+
+  if (subprocess == NULL)
     {
       g_assert (error != NULL);
       g_task_return_error (task, error);
@@ -555,7 +552,9 @@ ide_makecache_new_worker (GTask        *task,
   /*
    * Step 4, wait for the subprocess to complete.
    */
-  if (!g_subprocess_wait (subprocess, cancellable, &error))
+  IDE_TRACE_MSG ("waiting for process to exit");
+
+  if (!ide_subprocess_wait (subprocess, cancellable, &error))
     {
       g_assert (error != NULL);
       g_task_return_error (task, error);
@@ -585,9 +584,10 @@ ide_makecache_new_worker (GTask        *task,
   /*
    * Step 6, map the makecache file into memory.
    */
+  lseek (fd, 0, SEEK_SET);
   mapped = g_mapped_file_new_from_fd (fd, FALSE, &error);
 
-  if (!mapped)
+  if (mapped == NULL)
     {
       g_assert (error != NULL);
       g_task_return_error (task, error);
@@ -612,9 +612,10 @@ ide_makecache_new_worker (GTask        *task,
     }
 
   /*
-   * Step 9, save the mmap for future use.
+   * Step 9, save the mmap and runtime for future use.
    */
   self->mapped = g_mapped_file_ref (mapped);
+  self->runtime = g_object_ref (runtime);
 
   g_task_return_pointer (task, g_object_ref (self), g_object_unref);
 
@@ -909,8 +910,8 @@ ide_makecache_get_file_flags_worker (GTask        *task,
   for (j = 0; j < lookup->targets->len; j++)
     {
       IdeMakecacheTarget *target;
-      g_autoptr(GSubprocessLauncher) launcher = NULL;
-      g_autoptr(GSubprocess) subprocess = NULL;
+      g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+      g_autoptr(IdeSubprocess) subprocess = NULL;
       g_autoptr(GPtrArray) argv = NULL;
       g_autofree gchar *stdoutstr = NULL;
       g_autofree gchar *cwd = NULL;
@@ -966,13 +967,23 @@ ide_makecache_get_file_flags_worker (GTask        *task,
       }
 #endif
 
-      launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
-      g_subprocess_launcher_set_cwd (launcher, cwd);
-      subprocess = g_subprocess_launcher_spawnv (launcher,
-                                                 (const gchar * const *)argv->pdata,
-                                                 &error);
+      launcher = ide_runtime_create_launcher (lookup->self->runtime, &error);
+
+      if (launcher == NULL)
+        {
+          g_assert (error != NULL);
+          g_task_return_error (task, error);
+          IDE_EXIT;
+        }
+
+      ide_subprocess_launcher_set_flags (launcher, (G_SUBPROCESS_FLAGS_STDOUT_PIPE |
+                                                    G_SUBPROCESS_FLAGS_STDERR_SILENCE));
+      ide_subprocess_launcher_set_cwd (launcher, cwd);
+      ide_subprocess_launcher_push_args (launcher, (const gchar * const *)argv->pdata);
 
-      if (!subprocess)
+      subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, &error);
+
+      if (subprocess == NULL)
         {
           g_assert (error != NULL);
           g_task_return_error (task, error);
@@ -980,7 +991,7 @@ ide_makecache_get_file_flags_worker (GTask        *task,
         }
 
       /* Don't let ourselves be cancelled from this operation */
-      if (!g_subprocess_communicate_utf8 (subprocess, NULL, NULL, &stdoutstr, NULL, &error))
+      if (!ide_subprocess_communicate_utf8 (subprocess, NULL, NULL, &stdoutstr, NULL, &error))
         {
           g_assert (error != NULL);
           g_task_return_error (task, error);
@@ -1313,6 +1324,7 @@ ide_makecache_finalize (GObject *object)
   g_clear_pointer (&self->mapped, g_mapped_file_unref);
   g_clear_object (&self->file_targets_cache);
   g_clear_object (&self->file_flags_cache);
+  g_clear_object (&self->runtime);
   g_clear_pointer (&self->build_targets, g_ptr_array_unref);
 
   G_OBJECT_CLASS (ide_makecache_parent_class)->finalize (object);
@@ -1418,7 +1430,7 @@ ide_makecache_get_makefile (IdeMakecache *self)
 }
 
 void
-ide_makecache_new_for_makefile_async (IdeContext          *context,
+ide_makecache_new_for_makefile_async (IdeRuntime          *runtime,
                                       GFile               *makefile,
                                       GCancellable        *cancellable,
                                       GAsyncReadyCallback  callback,
@@ -1426,10 +1438,11 @@ ide_makecache_new_for_makefile_async (IdeContext          *context,
 {
   g_autoptr(GTask) task = NULL;
   g_autoptr(IdeMakecache) self = NULL;
+  IdeContext *context;
 
   IDE_ENTRY;
 
-  g_return_if_fail (IDE_IS_CONTEXT (context));
+  g_return_if_fail (IDE_IS_RUNTIME (runtime));
   g_return_if_fail (G_IS_FILE (makefile));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
@@ -1440,6 +1453,8 @@ ide_makecache_new_for_makefile_async (IdeContext          *context,
   }
 #endif
 
+  context = ide_object_get_context (IDE_OBJECT (runtime));
+
   self = g_object_new (IDE_TYPE_MAKECACHE,
                        "context", context,
                        "makefile", makefile,
@@ -1447,6 +1462,7 @@ ide_makecache_new_for_makefile_async (IdeContext          *context,
 
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, ide_makecache_new_for_makefile_async);
+  g_task_set_task_data (task, g_object_ref (runtime), g_object_unref);
 
   ide_thread_pool_push_task (IDE_THREAD_POOL_COMPILER,
                              task,
diff --git a/plugins/autotools/ide-makecache.h b/plugins/autotools/ide-makecache.h
index 1cbe65e..d069b48 100644
--- a/plugins/autotools/ide-makecache.h
+++ b/plugins/autotools/ide-makecache.h
@@ -19,7 +19,7 @@
 #ifndef IDE_MAKECACHE_H
 #define IDE_MAKECACHE_H
 
-#include "ide-object.h"
+#include <ide.h>
 
 #include "ide-makecache-target.h"
 
@@ -29,7 +29,7 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeMakecache, ide_makecache, IDE, MAKECACHE, IdeObject)
 
-void                 ide_makecache_new_for_makefile_async   (IdeContext           *context,
+void                 ide_makecache_new_for_makefile_async   (IdeRuntime           *runtime,
                                                              GFile                *makefile,
                                                              GCancellable         *cancellable,
                                                              GAsyncReadyCallback   callback,
diff --git a/plugins/cargo/cargo_plugin.py b/plugins/cargo/cargo_plugin.py
index 73fccc1..d42464f 100644
--- a/plugins/cargo/cargo_plugin.py
+++ b/plugins/cargo/cargo_plugin.py
@@ -65,32 +65,9 @@ class CargoBuildSystem(Ide.Object, Ide.BuildSystem, Gio.AsyncInitable):
         # Priority is used to determine the order of discovery
         return 2000
 
-    def do_get_build_flags_async(self, ifile, cancellable, callback, data):
-        # GTask sort of is painful from Python.
-        # We can use it to attach some data to return from the finish
-        # function though.
-        task = Gio.Task.new(self, cancellable, callback)
-        task.build_flags = []
-        task.return_boolean(True)
-
-    def do_get_build_flags_finish(self, result):
-        if task.propagate_boolean():
-            return result.build_flags
-
     def do_get_builder(self, config):
         return CargoBuilder(config, context=self.get_context())
 
-    def do_get_build_targets_async(self, cancellable, callback, data):
-        # TODO: We need a way to figure out what "cargo run" will do so that
-        #       we can synthesize that as a build result.
-        task = Gio.Task.new(self, cancellable, callback)
-        task.build_targets = []
-        task.return_boolean(True)
-
-    def do_get_build_targets_finish(self, task):
-        if task.propagate_boolean():
-            return task.build_targets
-
 class CargoBuilder(Ide.Builder):
     config = GObject.Property(type=Ide.Configuration)
 
@@ -151,6 +128,30 @@ class CargoBuilder(Ide.Builder):
         if task.propagate_boolean():
             return task.build_result
 
+    def do_get_build_flags_async(self, ifile, cancellable, callback, data):
+        # TODO:
+        # GTask sort of is painful from Python.
+        # We can use it to attach some data to return from the finish
+        # function though.
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_flags = []
+        task.return_boolean(True)
+
+    def do_get_build_flags_finish(self, result):
+        if task.propagate_boolean():
+            return result.build_flags
+
+    def do_get_build_targets_async(self, cancellable, callback, data):
+        # TODO: We need a way to figure out what "cargo run" will do so that
+        #       we can synthesize that as a build result.
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_targets = []
+        task.return_boolean(True)
+
+    def do_get_build_targets_finish(self, task):
+        if task.propagate_boolean():
+            return task.build_targets
+
 class CargoBuildResult(Ide.BuildResult):
     runtime = GObject.Property(type=Ide.Runtime)
 
diff --git a/plugins/git/ide-git-vcs.c b/plugins/git/ide-git-vcs.c
index 2e78992..a68af3c 100644
--- a/plugins/git/ide-git-vcs.c
+++ b/plugins/git/ide-git-vcs.c
@@ -166,6 +166,24 @@ ide_git_vcs_discover (IdeGitVcs  *self,
   if (g_strcmp0 (name, ".git") == 0)
     return g_object_ref (file);
 
+  /*
+   * Work around for in-tree tests which we do not
+   * want to use the git backend.
+   *
+   * TODO: Allow options during context creation.
+   */
+  child = g_file_get_child (file, ".you-dont-git-me");
+
+  if (g_file_query_exists (child, NULL))
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_NOT_SUPPORTED,
+                   "The project has blocked use of the git plugin");
+      return NULL;
+    }
+
+  g_clear_object (&child);
   child = g_file_get_child (file, ".git");
 
   if (g_file_query_exists (child, NULL))
@@ -198,12 +216,16 @@ ide_git_vcs_load (IdeGitVcs  *self,
   GFile *project_file;
 
   g_assert (IDE_IS_GIT_VCS (self));
+  g_assert (error != NULL);
 
   context = ide_object_get_context (IDE_OBJECT (self));
   project_file = ide_context_get_project_file (context);
 
   if (!(location = ide_git_vcs_discover (self, project_file, error)))
     {
+      if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+        return NULL;
+
       g_clear_error (error);
 
       /* Fallback to libgit2(-glib) discovery */
diff --git a/plugins/meson/meson_plugin/__init__.py b/plugins/meson/meson_plugin/__init__.py
index 4a989df..96610fb 100644
--- a/plugins/meson/meson_plugin/__init__.py
+++ b/plugins/meson/meson_plugin/__init__.py
@@ -43,9 +43,6 @@ class MesonBuildSystem(Ide.Object, Ide.BuildSystem, Gio.AsyncInitable):
         task = Gio.Task.new(self, cancel, callback)
         task.set_priority(priority)
 
-        self._cached_config = None
-        self._cached_builder = None
-
         # TODO: Be async here also
         project_file = self.get_context().get_project_file()
         if project_file.get_basename() == 'meson.build':
@@ -64,31 +61,101 @@ class MesonBuildSystem(Ide.Object, Ide.BuildSystem, Gio.AsyncInitable):
         return -200 # Lower priority than Autotools for now
 
     def do_get_builder(self, config):
-        if config == self._cached_config:
-            return self._cached_builder
-        else:
-            self._cached_config = config
-            self._cached_builder = MesonBuilder(context=self.get_context(), configuration=config)
-            return self._cached_builder
+        return MesonBuilder(context=self.get_context(), configuration=config)
 
-    def do_get_build_flags_async(self, ifile, cancellable, callback, data=None):
+
+class MesonBuilder(Ide.Builder):
+    configuration = GObject.Property(type=Ide.Configuration)
+
+    def __init__(self, **kwargs):
+        super().__init__(**kwargs)
+
+    def _get_build_dir(self) -> Gio.File:
+        context = self.get_context()
+
+        # This matches the Autotools layout
+        project_id = context.get_project().get_id()
+        buildroot = context.get_root_build_dir()
+        device = self.props.configuration.get_device()
+        device_id = device.get_id()
+        system_type = device.get_system_type()
+
+        return Gio.File.new_for_path(path.join(buildroot, project_id, device_id, system_type))
+
+    def _get_source_dir(self) -> Gio.File:
+        context = self.get_context()
+        return context.get_vcs().get_working_directory()
+
+    def do_build_async(self, flags, cancellable, callback, data=None):
         task = Gio.Task.new(self, cancellable, callback)
-        task.build_flags = []
+        task.build_result = MesonBuildResult(self.configuration,
+                                             self._get_build_dir(),
+                                             self._get_source_dir(),
+                                             cancellable,
+                                             flags=flags)
 
-        # TODO: Cleaner API for this? The builder has this information not us..
-        config = self._cached_config
-        builder = self._cached_builder
+        def wrap_build():
+            task.build_result.set_running(True)
+            try:
+                task.build_result.build()
+                task.build_result.set_mode(_('Successful'))
+                task.build_result.set_failed(False)
+                task.return_boolean(True)
+            except GLib.Error as e:
+                task.build_result.set_mode(_('Failed'))
+                task.build_result.set_failed(True)
+                task.return_error(e)
+            task.build_result.set_running(False)
+
+        thread = threading.Thread(target=wrap_build)
+        thread.start()
+
+        return task.build_result
+
+    def do_build_finish(self, result) -> Ide.BuildResult:
+        if result.propagate_boolean():
+            return result.build_result
+
+    def do_install_async(self, cancellable, callback, data=None):
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_result = MesonBuildResult(self.configuration,
+                                             self._get_build_dir(),
+                                             self._get_source_dir(),
+                                             cancellable)
+
+        def wrap_install():
+            task.build_result.set_running(True)
+            try:
+                task.build_result.install()
+                self = task.get_source_object()
+                task.build_result.set_mode(_('Successful'))
+                task.build_result.set_failed(False)
+                task.return_boolean(True)
+            except GLib.Error as e:
+                task.build_result.set_mode(_("Failed"))
+                task.build_result.set_failed(True)
+                task.return_error(e)
+            task.build_result.set_running(False)
 
-        if not config:
-            task.return_error(GLib.Error('Meson: Project must be built before we can get flags'))
-            return
+        thread = threading.Thread(target=wrap_install)
+        thread.start()
+
+        return task.build_result
+
+    def do_install_finish(self, result) -> Ide.BuildResult:
+        if result.propagate_boolean():
+            return result.build_result
+
+    def do_get_build_flags_async(self, ifile, cancellable, callback, data=None):
+        task = Gio.Task.new(self, cancellable, callback)
+        task.build_flags = []
 
         def extract_flags(command: str):
             flags = GLib.shell_parse_argv(command)[1] # Raises on failure
             return [flag for flag in flags if flag.startswith(('-I', '-isystem', '-W', '-D'))]
 
         def build_flags_thread():
-            commands_file = path.join(builder._get_build_dir().get_path(), 'compile_commands.json')
+            commands_file = path.join(self._get_build_dir().get_path(), 'compile_commands.json')
             try:
                 with open(commands_file) as f:
                     commands = json.loads(f.read(), encoding='utf-8')
@@ -122,15 +189,13 @@ class MesonBuildSystem(Ide.Object, Ide.BuildSystem, Gio.AsyncInitable):
         task = Gio.Task.new(self, cancellable, callback)
         task.build_targets = []
 
-        # TODO: Same API comment as above.
-        config = self._cached_config
-        builder = self._cached_builder
+        config = self.get_configuration()
 
         def build_targets_thread():
             # TODO: Ide.Subprocess.communicate_utf8(None, cancellable) doesn't work?
             try:
                 ret = subprocess.check_output(['mesonintrospect', '--targets',
-                                               builder._get_build_dir().get_path()])
+                                               self._get_build_dir().get_path()])
             except (subprocess.CalledProcessError, FileNotFoundError) as e:
                 task.return_error(GLib.Error('Failed to run mesonintrospect: {}'.format(e)))
                 return
@@ -173,89 +238,6 @@ class MesonBuildSystem(Ide.Object, Ide.BuildSystem, Gio.AsyncInitable):
             return result.build_targets
 
 
-class MesonBuilder(Ide.Builder):
-    configuration = GObject.Property(type=Ide.Configuration)
-
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
-
-    def _get_build_dir(self) -> Gio.File:
-        context = self.get_context()
-
-        # This matches the Autotools layout
-        project_id = context.get_project().get_id()
-        buildroot = context.get_root_build_dir()
-        device = self.props.configuration.get_device()
-        device_id = device.get_id()
-        system_type = device.get_system_type()
-
-        return Gio.File.new_for_path(path.join(buildroot, project_id, device_id, system_type))
-
-    def _get_source_dir(self) -> Gio.File:
-        context = self.get_context()
-        return context.get_vcs().get_working_directory()
-
-    def do_build_async(self, flags, cancellable, callback, data=None):
-        task = Gio.Task.new(self, cancellable, callback)
-        task.build_result = MesonBuildResult(self.configuration,
-                                             self._get_build_dir(),
-                                             self._get_source_dir(),
-                                             cancellable,
-                                             flags=flags)
-
-        def wrap_build():
-            task.build_result.set_running(True)
-            try:
-                task.build_result.build()
-                task.build_result.set_mode(_('Successful'))
-                task.build_result.set_failed(False)
-                task.return_boolean(True)
-            except GLib.Error as e:
-                task.build_result.set_mode(_('Failed'))
-                task.build_result.set_failed(True)
-                task.return_error(e)
-            task.build_result.set_running(False)
-
-        thread = threading.Thread(target=wrap_build)
-        thread.start()
-
-        return task.build_result
-
-    def do_build_finish(self, result) -> Ide.BuildResult:
-        if result.propagate_boolean():
-            return result.build_result
-
-    def do_install_async(self, cancellable, callback, data=None):
-        task = Gio.Task.new(self, cancellable, callback)
-        task.build_result = MesonBuildResult(self.configuration,
-                                             self._get_build_dir(),
-                                             self._get_source_dir(),
-                                             cancellable)
-
-        def wrap_install():
-            task.build_result.set_running(True)
-            try:
-                task.build_result.install()
-                self = task.get_source_object()
-                task.build_result.set_mode(_('Successful'))
-                task.build_result.set_failed(False)
-                task.return_boolean(True)
-            except GLib.Error as e:
-                task.build_result.set_mode(_("Failed"))
-                task.build_result.set_failed(True)
-                task.return_error(e)
-            task.build_result.set_running(False)
-
-        thread = threading.Thread(target=wrap_install)
-        thread.start()
-
-        return task.build_result
-
-    def do_install_finish(self, result) -> Ide.BuildResult:
-        if result.propagate_boolean():
-            return result.build_result
-
-
 class MesonBuildResult(Ide.BuildResult):
 
     def __init__(self, config, blddir, srcdir, cancel, flags=0, **kwargs):
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 789dc0c..bfe716f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -117,6 +117,12 @@ test_ide_buffer_CFLAGS = $(tests_cflags)
 test_ide_buffer_LDADD = $(tests_libs)
 
 
+TESTS += test-ide-builder
+test_ide_builder_SOURCES = test-ide-builder.c
+test_ide_builder_CFLAGS = $(tests_cflags)
+test_ide_builder_LDADD = $(tests_libs)
+
+
 TESTS += test-ide-doap
 test_ide_doap_SOURCES = test-ide-doap.c
 test_ide_doap_CFLAGS = $(tests_cflags)
@@ -272,10 +278,17 @@ endif
 check_PROGRAMS = $(TESTS) $(misc_programs)
 
 EXTRA_DIST += \
-       data/project1/configure.ac \
        data/project1/.editorconfig \
+       data/project1/.you-dont-git-me \
+       data/project1/autogen.sh \
+       data/project1/build-aux/m4/.keep \
+       data/project1/configure.ac \
        data/project1/project1.doap \
        data/project1/tags \
+       data/project2/.you-dont-git-me \
        $(NULL)
 
+run-%: %
+       $(TESTS_ENVIRONMENT) $(LIBTOOL) --mode=execute gdb -ex run $(builddir)/$*
+
 -include $(top_srcdir)/git.mk
diff --git a/tests/data/project1/.gitignore b/tests/data/project1/.gitignore
new file mode 100644
index 0000000..4852292
--- /dev/null
+++ b/tests/data/project1/.gitignore
@@ -0,0 +1,18 @@
+Makefile.in
+aclocal.m4
+autom4te.cache/
+build-aux/compile
+build-aux/config.guess
+build-aux/config.sub
+build-aux/depcomp
+build-aux/install-sh
+build-aux/ltmain.sh
+build-aux/m4/libtool.m4
+build-aux/m4/ltoptions.m4
+build-aux/m4/ltsugar.m4
+build-aux/m4/ltversion.m4
+build-aux/m4/lt~obsolete.m4
+build-aux/missing
+build/
+config.h.in
+configure
diff --git a/tests/data/project1/.you-dont-git-me b/tests/data/project1/.you-dont-git-me
new file mode 100644
index 0000000..e69de29
diff --git a/tests/data/project1/Makefile.am b/tests/data/project1/Makefile.am
new file mode 100644
index 0000000..12b3e01
--- /dev/null
+++ b/tests/data/project1/Makefile.am
@@ -0,0 +1,5 @@
+
+bin_PROGRAMS = project1
+
+project1_SOURCES = project1.c
+project1_CFLAGS = -D_THIS_IS_PROJECT1
diff --git a/tests/data/project1/autogen.sh b/tests/data/project1/autogen.sh
new file mode 100755
index 0000000..6d13746
--- /dev/null
+++ b/tests/data/project1/autogen.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+olddir=`pwd`
+cd $srcdir
+aclocal --install -I build-aux || exit 1
+autoreconf --force --install -Wno-portability || exit 1
+cd $olddir
diff --git a/tests/data/project1/build-aux/.gitignore b/tests/data/project1/build-aux/.gitignore
new file mode 100644
index 0000000..0f4126c
--- /dev/null
+++ b/tests/data/project1/build-aux/.gitignore
@@ -0,0 +1 @@
+*.m4
diff --git a/tests/data/project1/build-aux/m4/.keep b/tests/data/project1/build-aux/m4/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/tests/data/project1/configure.ac b/tests/data/project1/configure.ac
index 63f03a0..939437d 100644
--- a/tests/data/project1/configure.ac
+++ b/tests/data/project1/configure.ac
@@ -1 +1,11 @@
+AC_PREREQ([2.69])
+AC_INIT([project1], [0.1], [], [project1], [])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([build-aux/m4])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CANONICAL_HOST
+AM_INIT_AUTOMAKE([1.11 foreign subdir-objects tar-ustar no-dist-gzip dist-xz])
+LT_PREREQ([2.2])
 LT_INIT
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/tests/data/project1/project1.c b/tests/data/project1/project1.c
new file mode 100644
index 0000000..e69de29
diff --git a/tests/data/project2/.you-dont-git-me b/tests/data/project2/.you-dont-git-me
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test-ide-buffer-manager.c b/tests/test-ide-buffer-manager.c
index 6681bf2..408231d 100644
--- a/tests/test-ide-buffer-manager.c
+++ b/tests/test-ide-buffer-manager.c
@@ -88,7 +88,7 @@ test_buffer_manager_basic_cb2 (GObject      *object,
 
   gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end);
   text = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer), &begin, &end, TRUE);
-  g_assert_cmpstr (text, ==, "LT_INIT");
+  g_assert (g_str_has_prefix (text, "AC_PREREQ([2.69])\n"));
 
   tmpfd = g_file_open_tmp (NULL, &tmpfilename, &error);
   g_assert_no_error (error);
diff --git a/tests/test-ide-buffer.c b/tests/test-ide-buffer.c
index 19e6a13..e979664 100644
--- a/tests/test-ide-buffer.c
+++ b/tests/test-ide-buffer.c
@@ -25,30 +25,6 @@
 #include "application/ide-application-tests.h"
 
 static void
-flags_changed_cb (IdeBuffer *buffer,
-                  gpointer   user_data)
-{
-  g_autofree gchar *str = NULL;
-  g_autoptr(GTask) task = user_data;
-  GtkTextIter begin;
-  GtkTextIter end;
-
-  IDE_ENTRY;
-
-  ide_buffer_trim_trailing_whitespace (buffer);
-
-  gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &begin, &end);
-  str = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer), &begin, &end, TRUE);
-  g_assert_cmpstr (str, ==, "abcd\n\n\n");
-
-  g_task_return_boolean (task, TRUE);
-
-  g_object_unref (buffer);
-
-  IDE_EXIT;
-}
-
-static void
 test_buffer_basic_cb2 (GObject      *object,
                        GAsyncResult *result,
                        gpointer      user_data)
@@ -65,12 +41,7 @@ test_buffer_basic_cb2 (GObject      *object,
   g_assert (ret);
   g_assert (IDE_IS_BUFFER (ret));
 
-  g_signal_connect_object (ret,
-                           "line-flags-changed",
-                           G_CALLBACK (flags_changed_cb),
-                           g_object_ref (task),
-                           0);
-  gtk_text_buffer_set_text (GTK_TEXT_BUFFER (ret), "abcd  \n\n  \n", -1);
+  g_task_return_boolean (task, TRUE);
 
   IDE_EXIT;
 }
diff --git a/tests/test-ide-builder.c b/tests/test-ide-builder.c
new file mode 100644
index 0000000..8b994b7
--- /dev/null
+++ b/tests/test-ide-builder.c
@@ -0,0 +1,354 @@
+/* test-ide-builder.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <ide.h>
+
+#include "application/ide-application-tests.h"
+
+static void
+get_build_targets_cb (GObject      *object,
+                      GAsyncResult *result,
+                      gpointer      user_data)
+{
+  IdeBuilder *builder = (IdeBuilder *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GPtrArray) targets = NULL;
+
+  g_assert (IDE_IS_BUILDER (builder));
+  g_assert (G_IS_TASK (task));
+
+  targets = ide_builder_get_build_targets_finish (builder, result, &error);
+  g_assert_no_error (error);
+  g_assert (targets != NULL);
+
+  g_task_return_boolean (task, TRUE);
+}
+
+static void
+get_build_flags_cb (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  IdeBuilder *builder = (IdeBuilder *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_auto(GStrv) flags = NULL;
+  GCancellable *cancellable;
+  gboolean found = FALSE;
+
+  g_assert (IDE_IS_BUILDER (builder));
+  g_assert (G_IS_TASK (task));
+
+  flags = ide_builder_get_build_flags_finish (builder, result, &error);
+  g_assert_no_error (error);
+  g_assert (flags != NULL);
+  for (guint i = 0; flags[i]; i++)
+    found |= g_str_equal (flags[i], "-D_THIS_IS_PROJECT1");
+  g_assert_cmpint (found, ==, TRUE);
+
+  /* Now try to get the build targets */
+
+  cancellable = g_task_get_cancellable (task);
+
+  ide_builder_get_build_targets_async (builder,
+                                       cancellable,
+                                       get_build_targets_cb,
+                                       g_steal_pointer (&task));
+}
+
+static void
+build_cb (GObject      *object,
+          GAsyncResult *result,
+          gpointer      user_data)
+{
+  IdeBuilder *builder = (IdeBuilder *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(IdeBuildResult) build_result = NULL;
+  g_autoptr(IdeFile) file = NULL;
+  g_autoptr(GError) error = NULL;
+  GCancellable *cancellable;
+  IdeContext *context;
+  IdeProject *project;
+
+  build_result = ide_builder_build_finish (builder, result, &error);
+  g_assert_no_error (error);
+  g_assert (build_result != NULL);
+  g_assert (IDE_IS_BUILD_RESULT (build_result));
+
+  cancellable = g_task_get_cancellable (task);
+
+  context = ide_object_get_context (IDE_OBJECT (builder));
+
+  project = ide_context_get_project (context);
+  file = ide_project_get_file_for_path (project, "project1.c");
+
+  /* Now try to get the cflags for a file and ensure cflag extraction works */
+
+  ide_builder_get_build_flags_async (builder,
+                                     file,
+                                     cancellable,
+                                     get_build_flags_cb,
+                                     g_steal_pointer (&task));
+}
+
+static void
+context_loaded_cb (GObject      *object,
+                   GAsyncResult *result,
+                   gpointer      user_data)
+{
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(IdeContext) context = NULL;
+  g_autoptr(IdeBuilder) builder = NULL;
+  g_autoptr(IdeConfiguration) config = NULL;
+  IdeBuildSystem *build_system;
+  GCancellable *cancellable;
+  GError *error = NULL;
+  IdeVcs *vcs;
+  GFile *workdir;
+  g_autofree gchar *name = NULL;
+
+  cancellable = g_task_get_cancellable (task);
+
+  context = ide_context_new_finish (result, &error);
+  g_assert_no_error (error);
+  g_assert (context != NULL);
+  g_assert (IDE_IS_CONTEXT (context));
+
+  vcs = ide_context_get_vcs (context);
+  g_assert_cmpstr ("IdeDirectoryVcs", ==, G_OBJECT_TYPE_NAME (vcs));
+
+  workdir = ide_vcs_get_working_directory (vcs);
+  name = g_file_get_basename (workdir);
+  g_assert_cmpstr (name, ==, "project1");
+
+
+  build_system = ide_context_get_build_system (context);
+  g_assert (IDE_IS_BUILD_SYSTEM (build_system));
+  g_assert_cmpstr ("IdeAutotoolsBuildSystem", ==, G_OBJECT_TYPE_NAME (build_system));
+
+  config = g_object_new (IDE_TYPE_CONFIGURATION,
+                         "id", "test-build",
+                         "app-id", "org.gnome.Project1",
+                         "context", context,
+                         "runtime-id", "host",
+                         "device-id", "local",
+                         NULL);
+
+  ide_configuration_set_dirty (config, FALSE);
+  g_assert_cmpint (FALSE, ==, ide_configuration_get_dirty (config));
+
+  builder = ide_build_system_get_builder (build_system, config, &error);
+  g_assert_no_error (error);
+  g_assert (builder != NULL);
+  g_assert (IDE_IS_BUILDER (builder));
+  g_assert_cmpstr ("IdeAutotoolsBuilder", ==, G_OBJECT_TYPE_NAME (builder));
+
+  /* Do a "build" that will only do autogen/configure and no gmake */
+  ide_builder_build_async (builder,
+                           IDE_BUILDER_BUILD_FLAGS_NO_BUILD,
+                           NULL,
+                           cancellable,
+                           build_cb,
+                           g_steal_pointer (&task));
+}
+
+static void
+test_build_system_autotools (GCancellable        *cancellable,
+                             GAsyncReadyCallback  callback,
+                             gpointer             user_data)
+{
+  g_autoptr(GFile) project_file = NULL;
+  g_autofree gchar *path = NULL;
+  const gchar *srcdir = g_getenv ("G_TEST_SRCDIR");
+  g_autoptr(GTask) task = NULL;
+
+  task = g_task_new (NULL, cancellable, callback, user_data);
+
+  path = g_build_filename (srcdir, "data", "project1", "configure.ac", NULL);
+  project_file = g_file_new_for_path (path);
+
+  ide_context_new_async (project_file,
+                         cancellable,
+                         context_loaded_cb,
+                         g_object_ref (task));
+}
+
+static void
+project2_get_build_flags_cb (GObject      *object,
+                             GAsyncResult *result,
+                             gpointer      user_data)
+{
+  IdeBuilder *builder = (IdeBuilder *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_auto(GStrv) flags = NULL;
+  gboolean found = FALSE;
+
+  g_assert (IDE_IS_BUILDER (builder));
+  g_assert (G_IS_TASK (task));
+
+  flags = ide_builder_get_build_flags_finish (builder, result, &error);
+  g_assert_no_error (error);
+  g_assert (flags != NULL);
+  for (guint i = 0; flags[i]; i++)
+    found |= g_str_equal (flags[i], "-D_THIS_IS_PROJECT2");
+  g_assert_cmpint (found, ==, TRUE);
+
+  g_task_return_boolean (task, TRUE);
+}
+
+static void
+project2_build_cb (GObject      *object,
+                   GAsyncResult *result,
+                   gpointer      user_data)
+{
+  IdeBuilder *builder = (IdeBuilder *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(IdeBuildResult) build_result = NULL;
+  g_autoptr(IdeFile) file = NULL;
+  g_autoptr(GError) error = NULL;
+  GCancellable *cancellable;
+  IdeContext *context;
+  IdeProject *project;
+
+  build_result = ide_builder_build_finish (builder, result, &error);
+  g_assert (error != NULL);
+  g_assert (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED));
+  g_assert (build_result == NULL);
+
+  cancellable = g_task_get_cancellable (task);
+
+  context = ide_object_get_context (IDE_OBJECT (builder));
+
+  project = ide_context_get_project (context);
+  file = ide_project_get_file_for_path (project, "project2.c");
+
+  /* Now try to get the cflags for a file and ensure cflag extraction works */
+
+  ide_builder_get_build_flags_async (builder,
+                                     file,
+                                     cancellable,
+                                     project2_get_build_flags_cb,
+                                     g_steal_pointer (&task));
+}
+
+static void
+directory_context_loaded (GObject      *object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(IdeContext) context = NULL;
+  g_autoptr(IdeBuilder) builder = NULL;
+  g_autoptr(IdeConfiguration) config = NULL;
+  IdeBuildSystem *build_system;
+  GCancellable *cancellable;
+  GError *error = NULL;
+  IdeVcs *vcs;
+  GFile *workdir;
+  g_autofree gchar *name = NULL;
+
+  cancellable = g_task_get_cancellable (task);
+
+  context = ide_context_new_finish (result, &error);
+  g_assert_no_error (error);
+  g_assert (context != NULL);
+  g_assert (IDE_IS_CONTEXT (context));
+
+  vcs = ide_context_get_vcs (context);
+  g_assert_cmpstr ("IdeDirectoryVcs", ==, G_OBJECT_TYPE_NAME (vcs));
+
+  workdir = ide_vcs_get_working_directory (vcs);
+  name = g_file_get_basename (workdir);
+  g_assert_cmpstr (name, ==, "project2");
+
+
+  build_system = ide_context_get_build_system (context);
+  g_assert (IDE_IS_BUILD_SYSTEM (build_system));
+  g_assert_cmpstr ("IdeDirectoryBuildSystem", ==, G_OBJECT_TYPE_NAME (build_system));
+
+  config = g_object_new (IDE_TYPE_CONFIGURATION,
+                         "id", "test-build",
+                         "app-id", "org.gnome.Project2",
+                         "context", context,
+                         "runtime-id", "host",
+                         "device-id", "local",
+                         NULL);
+
+  ide_configuration_setenv (config, "CFLAGS", "-D_THIS_IS_PROJECT2");
+
+  ide_configuration_set_dirty (config, FALSE);
+  g_assert_cmpint (FALSE, ==, ide_configuration_get_dirty (config));
+
+  builder = ide_build_system_get_builder (build_system, config, &error);
+  g_assert_no_error (error);
+  g_assert (builder != NULL);
+  g_assert (IDE_IS_BUILDER (builder));
+  g_assert_cmpstr ("IdeSimpleBuilder", ==, G_OBJECT_TYPE_NAME (builder));
+
+  ide_builder_build_async (builder,
+                           IDE_BUILDER_BUILD_FLAGS_NONE,
+                           NULL,
+                           cancellable,
+                           project2_build_cb,
+                           g_steal_pointer (&task));
+}
+
+static void
+test_build_system_directory (GCancellable        *cancellable,
+                             GAsyncReadyCallback  callback,
+                             gpointer             user_data)
+{
+  g_autoptr(GFile) project_file = NULL;
+  g_autofree gchar *path = NULL;
+  const gchar *srcdir = g_getenv ("G_TEST_SRCDIR");
+  g_autoptr(GTask) task = NULL;
+
+  task = g_task_new (NULL, cancellable, callback, user_data);
+
+  path = g_build_filename (srcdir, "data", "project2", NULL);
+  project_file = g_file_new_for_path (path);
+
+  ide_context_new_async (project_file,
+                         cancellable,
+                         directory_context_loaded,
+                         g_object_ref (task));
+}
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+  IdeApplication *app;
+  gint ret;
+
+  g_test_init (&argc, &argv, NULL);
+
+  ide_log_init (TRUE, NULL);
+  ide_log_set_verbosity (4);
+
+  app = ide_application_new ();
+  ide_application_add_test (app, "/Ide/BuildSystem/autotools", test_build_system_autotools, NULL);
+  ide_application_add_test (app, "/Ide/BuildSystem/directory", test_build_system_directory, NULL);
+  ret = g_application_run (G_APPLICATION (app), argc, argv);
+  g_object_unref (app);
+
+  return ret;
+}
diff --git a/tests/test-ide-context.c b/tests/test-ide-context.c
index 4755298..e2103ac 100644
--- a/tests/test-ide-context.c
+++ b/tests/test-ide-context.c
@@ -40,7 +40,7 @@ test_new_async_cb1 (GObject      *object,
   g_assert_cmpstr (G_OBJECT_TYPE_NAME (bs), ==, "IdeAutotoolsBuildSystem");
 
   vcs = ide_context_get_vcs (context);
-  g_assert_cmpstr (G_OBJECT_TYPE_NAME (vcs), ==, "IdeGitVcs");
+  g_assert_cmpstr (G_OBJECT_TYPE_NAME (vcs), ==, "IdeDirectoryVcs");
 
   root_build_dir = ide_context_get_root_build_dir (context);
   g_assert (g_str_has_suffix (root_build_dir, "/gnome-builder/builds"));
diff --git a/tests/test-ide-subprocess-launcher.c b/tests/test-ide-subprocess-launcher.c
index d6e7910..7d592ba 100644
--- a/tests/test-ide-subprocess-launcher.c
+++ b/tests/test-ide-subprocess-launcher.c
@@ -36,6 +36,30 @@ test_basic (void)
   g_assert_cmpint (ide_subprocess_wait_check (process, NULL, &error), !=, 0);
 }
 
+static void
+test_communicate (void)
+{
+  IdeSubprocessLauncher *launcher;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autofree gchar *stdout_buf = NULL;
+  gboolean r;
+
+  launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
+  ide_subprocess_launcher_push_argv (launcher, "ls");
+
+  subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error);
+  g_assert_no_error (error);
+  g_assert (subprocess != NULL);
+
+  r = ide_subprocess_communicate_utf8 (subprocess, NULL, NULL, &stdout_buf, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (r, ==, TRUE);
+
+  g_assert (stdout_buf != NULL);
+  g_assert (g_utf8_validate (stdout_buf, -1, NULL));
+}
+
 static int
 check_args (IdeSubprocessLauncher *launcher,
             gchar *argv0,
@@ -79,9 +103,11 @@ static void
 test_argv_manipulation (void)
 {
   g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autofree gchar *popped = NULL;
 
   launcher = ide_subprocess_launcher_new (0);
   g_assert (launcher != NULL);
+  g_object_add_weak_pointer (G_OBJECT (launcher), (gpointer *)&launcher);
 
   ide_subprocess_launcher_push_argv (launcher, "echo");
   ide_subprocess_launcher_push_argv (launcher, "world");
@@ -91,8 +117,12 @@ test_argv_manipulation (void)
   ide_subprocess_launcher_replace_argv (launcher, 2, "universe");
   g_assert_cmpint (check_args (launcher, "echo", "hello", "universe", NULL), !=, 0);
 
-  g_assert_cmpstr (ide_subprocess_launcher_pop_argv (launcher), ==, "universe");
+  popped = ide_subprocess_launcher_pop_argv (launcher);
+  g_assert_cmpstr (popped, ==, "universe");
   g_assert_cmpint (check_args (launcher, "echo", "hello", NULL), !=, 0);
+
+  g_object_unref (launcher);
+  g_assert (launcher == NULL);
 }
 
 gint
@@ -101,6 +131,7 @@ main (gint   argc,
 {
   g_test_init (&argc, &argv, NULL);
   g_test_add_func ("/Ide/SubprocessLauncher/basic", test_basic);
+  g_test_add_func ("/Ide/SubprocessLauncher/communicate", test_communicate);
   g_test_add_func ("/Ide/SubprocessLauncher/argv-manipulation", test_argv_manipulation);
   return g_test_run ();
 }


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