[gnome-builder/wip/chergert/buildcleanup: 9/10] build-system: move build helpers to IdeBuilder



commit dea27861880f05c70b9d893ab89b6308d4a2eee3
Author: Christian Hergert <chergert redhat com>
Date:   Sun Dec 11 19:27:57 2016 -0800

    build-system: move build helpers to IdeBuilder
    
    Instead of having get_build_flags_async() and get_build_targets_async()
    as part of the IdeBuildSystem, we've moved it to IdeBuilder. This allows
    the build system to have an active configuration which is necessary for
    many build systems to get accurate information.

 libide/Makefile.am                             |    2 +
 libide/buildsystem/ide-build-system.c          |  248 +++++++-----
 libide/buildsystem/ide-build-system.h          |   45 +--
 libide/buildsystem/ide-builder.c               |  203 +++++++++-
 libide/buildsystem/ide-builder.h               |  100 +++--
 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 +
 plugins/autotools/ide-autotools-build-system.c |  510 ++----------------------
 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/meson/meson_plugin/__init__.py         |  192 ++++-----
 tests/Makefile.am                              |    6 +
 tests/test-ide-builder.c                       |  354 ++++++++++++++++
 17 files changed, 1716 insertions(+), 899 deletions(-)
---
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..ca00818 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,18 @@ ide_builder_set_configuration (IdeBuilder       *self,
 
   g_assert (IDE_IS_BUILDER (self));
   g_assert (!configuration || IDE_IS_CONFIGURATION (configuration));
-
-  if (g_set_object (&priv->configuration, configuration))
-    g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_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).
+   *
+   * When the dirty bit is cleared from a successful build, the
+   * configuration will propagate that to the original build
+   * configuration.
+   */
+
+  priv->configuration = ide_configuration_snapshot (configuration);
 }
 
 static void
@@ -83,6 +97,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 +117,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 +207,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 +286,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 +314,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 +394,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-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/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-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/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 967a051..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)
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;
+}


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