[gnome-builder/wip/chergert/pipeline: 41/43] autotools: port to IdeBuildPipeline



commit fd30bc88e66c56912a67d07c01531b3972fbcabf
Author: Christian Hergert <chergert redhat com>
Date:   Fri Feb 3 14:03:30 2017 -0800

    autotools: port to IdeBuildPipeline
    
    This removes the vast amount of tricky code we previously had and moves
    it into a series of build stages to be attached to the build pipeline.
    
    As the configuration will not change during the lifetime of the pipeline,
    all of the work can be performed up front which should simplify some of
    our issues with threading in the build process (by making it go away).
    
    We can probably still simplify this further, but this at least gets
    everything working.
    
    I would like to see a transient stage added for running `make dist` to
    perform tarball creation.

 plugins/autotools/Makefile.am                      |   10 +-
 plugins/autotools/autotools-plugin.c               |    2 +
 .../autotools/ide-autotools-application-addin.c    |    4 +
 plugins/autotools/ide-autotools-autogen-stage.c    |  179 +++
 ...ols-builder.h => ide-autotools-autogen-stage.h} |   17 +-
 plugins/autotools/ide-autotools-build-system.c     |  359 +++++-
 plugins/autotools/ide-autotools-build-task.c       | 1266 --------------------
 plugins/autotools/ide-autotools-build-task.h       |   55 -
 plugins/autotools/ide-autotools-builder.c          |  802 -------------
 plugins/autotools/ide-autotools-makecache-stage.c  |  253 ++++
 plugins/autotools/ide-autotools-makecache-stage.h  |   38 +
 plugins/autotools/ide-autotools-pipeline-addin.c   |  427 +++++++
 ...ls-builder.h => ide-autotools-pipeline-addin.h} |   17 +-
 13 files changed, 1256 insertions(+), 2173 deletions(-)
---
diff --git a/plugins/autotools/Makefile.am b/plugins/autotools/Makefile.am
index 09e4ebb..b9af5a5 100644
--- a/plugins/autotools/Makefile.am
+++ b/plugins/autotools/Makefile.am
@@ -8,14 +8,16 @@ libautotools_plugin_la_SOURCES = \
        autotools-plugin.c \
        ide-autotools-application-addin.c \
        ide-autotools-application-addin.h \
-       ide-autotools-builder.c \
-       ide-autotools-builder.h \
+       ide-autotools-autogen-stage.c \
+       ide-autotools-autogen-stage.h \
        ide-autotools-build-system.c \
        ide-autotools-build-system.h \
        ide-autotools-build-target.c \
        ide-autotools-build-target.h \
-       ide-autotools-build-task.c \
-       ide-autotools-build-task.h \
+       ide-autotools-makecache-stage.c \
+       ide-autotools-makecache-stage.h \
+       ide-autotools-pipeline-addin.c \
+       ide-autotools-pipeline-addin.h \
        ide-autotools-project-miner.c \
        ide-autotools-project-miner.h \
        ide-makecache.c \
diff --git a/plugins/autotools/autotools-plugin.c b/plugins/autotools/autotools-plugin.c
index 9e15698..c1f94e7 100644
--- a/plugins/autotools/autotools-plugin.c
+++ b/plugins/autotools/autotools-plugin.c
@@ -21,12 +21,14 @@
 
 #include "ide-autotools-application-addin.h"
 #include "ide-autotools-build-system.h"
+#include "ide-autotools-pipeline-addin.h"
 #include "ide-autotools-project-miner.h"
 
 void
 peas_register_types (PeasObjectModule *module)
 {
   peas_object_module_register_extension_type (module, IDE_TYPE_APPLICATION_ADDIN, 
IDE_TYPE_AUTOTOOLS_APPLICATION_ADDIN);
+  peas_object_module_register_extension_type (module, IDE_TYPE_BUILD_PIPELINE_ADDIN, 
IDE_TYPE_AUTOTOOLS_PIPELINE_ADDIN);
   peas_object_module_register_extension_type (module, IDE_TYPE_BUILD_SYSTEM, 
IDE_TYPE_AUTOTOOLS_BUILD_SYSTEM);
   peas_object_module_register_extension_type (module, IDE_TYPE_PROJECT_MINER, 
IDE_TYPE_AUTOTOOLS_PROJECT_MINER);
 }
diff --git a/plugins/autotools/ide-autotools-application-addin.c 
b/plugins/autotools/ide-autotools-application-addin.c
index 51917fd..ac55f6a 100644
--- a/plugins/autotools/ide-autotools-application-addin.c
+++ b/plugins/autotools/ide-autotools-application-addin.c
@@ -38,6 +38,10 @@ ide_autotools_application_addin_load (IdeApplicationAddin *addin,
   g_assert (IDE_IS_AUTOTOOLS_APPLICATION_ADDIN (addin));
   g_assert (IDE_IS_APPLICATION (application));
 
+  /*
+   * TODO: Move this to an IdeDirectoryReaper
+   */
+
   path = g_build_filename (g_get_user_cache_dir (),
                            "gnome-builder",
                            "makecache",
diff --git a/plugins/autotools/ide-autotools-autogen-stage.c b/plugins/autotools/ide-autotools-autogen-stage.c
new file mode 100644
index 0000000..99dc54e
--- /dev/null
+++ b/plugins/autotools/ide-autotools-autogen-stage.c
@@ -0,0 +1,179 @@
+/* ide-autotools-autogen-stage.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-autotools-autogen-stage"
+
+#include "ide-autotools-autogen-stage.h"
+
+struct _IdeAutotoolsAutogenStage
+{
+  IdeBuildStage parent_instance;
+
+  gchar *srcdir;
+};
+
+G_DEFINE_TYPE (IdeAutotoolsAutogenStage, ide_autotools_autogen_stage, IDE_TYPE_BUILD_STAGE)
+
+enum {
+  PROP_0,
+  PROP_SRCDIR,
+  N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_autotools_autogen_stage_wait_check_cb (GObject      *object,
+                                           GAsyncResult *result,
+                                           gpointer      user_data)
+{
+  IdeSubprocess *subprocess = (IdeSubprocess *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_SUBPROCESS (subprocess));
+  g_assert (G_IS_TASK (task));
+
+  if (!ide_subprocess_wait_check_finish (subprocess, result, &error))
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+static void
+ide_autotools_autogen_stage_execute_async (IdeBuildStage       *stage,
+                                           IdeBuildPipeline    *pipeline,
+                                           GCancellable        *cancellable,
+                                           GAsyncReadyCallback  callback,
+                                           gpointer             user_data)
+{
+  IdeAutotoolsAutogenStage *self = (IdeAutotoolsAutogenStage *)stage;
+  g_autofree gchar *autogen_path = NULL;
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_AUTOTOOLS_AUTOGEN_STAGE (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_autotools_autogen_stage_execute_async);
+
+  autogen_path = g_build_filename (self->srcdir, "autogen.sh", NULL);
+
+  launcher = ide_build_pipeline_create_launcher (pipeline, &error);
+
+  if (launcher == NULL)
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  ide_subprocess_launcher_set_cwd (launcher, self->srcdir);
+
+  if (g_file_test (autogen_path, G_FILE_TEST_IS_REGULAR))
+    {
+      ide_subprocess_launcher_push_argv (launcher, autogen_path);
+      ide_subprocess_launcher_setenv (launcher, "NOCONFIGURE", "1", TRUE);
+    }
+  else
+    {
+      ide_subprocess_launcher_push_argv (launcher, "autoreconf");
+      ide_subprocess_launcher_push_argv (launcher, "-fiv");
+    }
+
+  subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, &error);
+
+  if (subprocess == NULL)
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  ide_build_stage_log_subprocess (stage, subprocess);
+
+  ide_subprocess_wait_check_async (subprocess,
+                                   cancellable,
+                                   ide_autotools_autogen_stage_wait_check_cb,
+                                   g_steal_pointer (&task));
+}
+
+static gboolean
+ide_autotools_autogen_stage_execute_finish (IdeBuildStage  *stage,
+                                            GAsyncResult   *result,
+                                            GError        **error)
+{
+  g_assert (IDE_IS_AUTOTOOLS_AUTOGEN_STAGE (stage));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+ide_autotools_autogen_stage_finalize (GObject *object)
+{
+  IdeAutotoolsAutogenStage *self = (IdeAutotoolsAutogenStage *)object;
+
+  g_clear_pointer (&self->srcdir, g_free);
+
+  G_OBJECT_CLASS (ide_autotools_autogen_stage_parent_class)->finalize (object);
+}
+
+static void
+ide_autotools_autogen_stage_set_property (GObject      *object,
+                                          guint         prop_id,
+                                          const GValue *value,
+                                          GParamSpec   *pspec)
+{
+  IdeAutotoolsAutogenStage *self = (IdeAutotoolsAutogenStage *)object;
+
+  switch (prop_id)
+    {
+    case PROP_SRCDIR:
+      self->srcdir = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_autotools_autogen_stage_class_init (IdeAutotoolsAutogenStageClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeBuildStageClass *stage_class = IDE_BUILD_STAGE_CLASS (klass);
+
+  object_class->finalize = ide_autotools_autogen_stage_finalize;
+  object_class->set_property = ide_autotools_autogen_stage_set_property;
+
+  stage_class->execute_async = ide_autotools_autogen_stage_execute_async;
+  stage_class->execute_finish = ide_autotools_autogen_stage_execute_finish;
+
+  properties [PROP_SRCDIR] =
+    g_param_spec_string ("srcdir", NULL, NULL, NULL,
+                         G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+  
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_autotools_autogen_stage_init (IdeAutotoolsAutogenStage *self)
+{
+}
diff --git a/plugins/autotools/ide-autotools-builder.h b/plugins/autotools/ide-autotools-autogen-stage.h
similarity index 57%
copy from plugins/autotools/ide-autotools-builder.h
copy to plugins/autotools/ide-autotools-autogen-stage.h
index 28be301..5dd2c6c 100644
--- a/plugins/autotools/ide-autotools-builder.h
+++ b/plugins/autotools/ide-autotools-autogen-stage.h
@@ -1,6 +1,6 @@
-/* ide-autotools-builder.h
+/* ide-autotools-autogen-stage.h
  *
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * 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
@@ -16,20 +16,17 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef IDE_AUTOTOOLS_BUILDER_H
-#define IDE_AUTOTOOLS_BUILDER_H
+#ifndef IDE_AUTOTOOLS_AUTOGEN_STAGE_H
+#define IDE_AUTOTOOLS_AUTOGEN_STAGE_H
 
 #include <ide.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_AUTOTOOLS_BUILDER (ide_autotools_builder_get_type())
+#define IDE_TYPE_AUTOTOOLS_AUTOGEN_STAGE (ide_autotools_autogen_stage_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeAutotoolsBuilder, ide_autotools_builder, IDE, AUTOTOOLS_BUILDER, IdeBuilder)
-
-GFile    *ide_autotools_builder_get_build_directory (IdeAutotoolsBuilder  *self);
-gboolean  ide_autotools_builder_get_needs_bootstrap (IdeAutotoolsBuilder  *self);
+G_DECLARE_FINAL_TYPE (IdeAutotoolsAutogenStage, ide_autotools_autogen_stage, IDE, AUTOTOOLS_AUTOGEN_STAGE, 
IdeBuildStage)
 
 G_END_DECLS
 
-#endif /* IDE_AUTOTOOLS_BUILDER_H */
+#endif /* IDE_AUTOTOOLS_AUTOGEN_STAGE_H */
diff --git a/plugins/autotools/ide-autotools-build-system.c b/plugins/autotools/ide-autotools-build-system.c
index 2b6e40e..159f225 100644
--- a/plugins/autotools/ide-autotools-build-system.c
+++ b/plugins/autotools/ide-autotools-build-system.c
@@ -25,7 +25,7 @@
 #include <ide.h>
 
 #include "ide-autotools-build-system.h"
-#include "ide-autotools-builder.h"
+#include "ide-autotools-makecache-stage.h"
 #include "ide-makecache.h"
 
 struct _IdeAutotoolsBuildSystem
@@ -64,30 +64,6 @@ ide_autotools_build_system_get_tarball_name (IdeAutotoolsBuildSystem *self)
   return self->tarball_name;
 }
 
-static IdeBuilder *
-ide_autotools_build_system_get_builder (IdeBuildSystem    *build_system,
-                                        IdeConfiguration  *configuration,
-                                        GError           **error)
-{
-  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);
-
-  IDE_RETURN (ret);
-}
-
 static gboolean
 is_configure (GFile *file)
 {
@@ -323,6 +299,333 @@ ide_autotools_build_system_get_priority (IdeBuildSystem *system)
 }
 
 static void
+find_makecache_stage (gpointer data,
+                      gpointer user_data)
+{
+  IdeMakecache **makecache = user_data;
+  IdeBuildStage *stage = data;
+
+  if (*makecache != NULL)
+    return;
+
+  if (IDE_IS_AUTOTOOLS_MAKECACHE_STAGE (stage))
+    *makecache = ide_autotools_makecache_stage_get_makecache (IDE_AUTOTOOLS_MAKECACHE_STAGE (stage));
+}
+
+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;
+  g_autoptr(GError) error = NULL;
+  g_auto(GStrv) flags = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAKECACHE (makecache));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  flags = ide_makecache_get_file_flags_finish (makecache, result, &error);
+
+  if (flags == NULL)
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_pointer (task, g_steal_pointer (&flags), (GDestroyNotify)g_strfreev);
+
+  IDE_EXIT;
+}
+
+static void
+ide_autotools_build_system_get_build_flags_execute_cb (GObject      *object,
+                                                       GAsyncResult *result,
+                                                       gpointer      user_data)
+{
+  IdeBuildManager *build_manager = (IdeBuildManager *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  IdeMakecache *makecache = NULL;
+  IdeBuildPipeline *pipeline;
+  GCancellable *cancellable;
+  GFile *file;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_BUILD_MANAGER (build_manager));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  file = g_task_get_task_data (task);
+  cancellable = g_task_get_cancellable (task);
+
+  g_assert (G_IS_FILE (file));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  if (!ide_build_manager_execute_finish (build_manager, result, &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  pipeline = ide_build_manager_get_pipeline (build_manager);
+
+  /*
+   * Locate our makecache by finding the makecache stage (which should have
+   * successfully executed by now) and get makecache object. Then we can
+   * locate the build flags for the file (which makecache will translate
+   * into the appropriate build target).
+   */
+
+  ide_build_pipeline_foreach_stage (pipeline, find_makecache_stage, &makecache);
+
+  if (makecache != NULL)
+    {
+      ide_makecache_get_file_flags_async (makecache,
+                                          file,
+                                          cancellable,
+                                          ide_autotools_build_system_get_file_flags_cb,
+                                          g_steal_pointer (&task));
+      IDE_EXIT;
+    }
+
+  /*
+   * We failed to locate anything, so just return an empty array of
+   * of flags.
+   */
+
+  g_task_return_pointer (task, g_new0 (gchar *, 1), g_free);
+
+  IDE_EXIT;
+}
+
+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;
+  IdeBuildManager *build_manager;
+  IdeContext *context;
+  g_autoptr(GTask) task = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_AUTOTOOLS_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_task_set_source_tag (task, ide_autotools_build_system_get_build_flags_async);
+  g_task_set_task_data (task, g_object_ref (ide_file_get_file (file)), g_object_unref);
+
+  /*
+   * To get the build flags for the file, we first need to get the makecache
+   * for the current build pipeline. That requires advancing the pipeline to
+   * at least the CONFIGURE stage so that our CONFIGURE|AFTER step has executed
+   * to generate the Makecache file in $builddir. With that, we can load a new
+   * IdeMakecache (if necessary) and scan the file for build flags.
+   */
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  build_manager = ide_context_get_build_manager (context);
+
+  ide_build_manager_execute_async (build_manager,
+                                   IDE_BUILD_PHASE_CONFIGURE,
+                                   cancellable,
+                                   ide_autotools_build_system_get_build_flags_execute_cb,
+                                   g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static gchar **
+ide_autotools_build_system_get_build_flags_finish (IdeBuildSystem  *build_system,
+                                                   GAsyncResult    *result,
+                                                   GError         **error)
+{
+  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (build_system));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+ide_autotools_build_system_get_build_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;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_MAKECACHE (makecache));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  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_build_system_get_build_targets_execute_cb (GObject      *object,
+                                                         GAsyncResult *result,
+                                                         gpointer      user_data)
+{
+  IdeBuildManager *build_manager = (IdeBuildManager *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) builddir_file = NULL;
+  IdeMakecache *makecache = NULL;
+  IdeBuildPipeline *pipeline;
+  GCancellable *cancellable;
+  const gchar *builddir;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_BUILD_MANAGER (build_manager));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  cancellable = g_task_get_cancellable (task);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  if (!ide_build_manager_execute_finish (build_manager, result, &error))
+    {
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  pipeline = ide_build_manager_get_pipeline (build_manager);
+
+  builddir = ide_build_pipeline_get_builddir (pipeline);
+  builddir_file = g_file_new_for_path (builddir);
+
+  /*
+   * Locate our makecache by finding the makecache stage (which should have
+   * successfully executed by now) and get makecache object. Then we can
+   * locate the build flags for the file (which makecache will translate
+   * into the appropriate build target).
+   */
+
+  ide_build_pipeline_foreach_stage (pipeline, find_makecache_stage, &makecache);
+
+  if (makecache != NULL)
+    {
+      ide_makecache_get_build_targets_async (makecache,
+                                             builddir_file,
+                                             cancellable,
+                                             ide_autotools_build_system_get_build_targets_cb,
+                                             g_steal_pointer (&task));
+      IDE_EXIT;
+    }
+
+  /*
+   * We failed to locate anything, so just return an empty array of
+   * of flags.
+   */
+
+  g_task_return_pointer (task, g_ptr_array_new (), (GDestroyNotify)g_ptr_array_unref);
+
+  IDE_EXIT;
+}
+
+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;
+  IdeBuildManager *build_manager;
+  IdeContext *context;
+  g_autoptr(GTask) task = NULL;
+
+  IDE_ENTRY;
+
+  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);
+
+  /*
+   * To get the build targets we first need to get the makecache for the
+   * current build pipeline. That requires advancing the pipeline to at least
+   * the CONFIGURE stage so that our CONFIGURE|AFTER step has executed to
+   * generate the Makecache file in $builddir.
+   */
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  build_manager = ide_context_get_build_manager (context);
+
+  ide_build_manager_execute_async (build_manager,
+                                   IDE_BUILD_PHASE_CONFIGURE,
+                                   cancellable,
+                                   ide_autotools_build_system_get_build_targets_execute_cb,
+                                   g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static GPtrArray *
+ide_autotools_build_system_get_build_targets_finish (IdeBuildSystem  *build_system,
+                                                     GAsyncResult    *result,
+                                                     GError         **error)
+{
+  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (build_system));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static gchar *
+ide_autotools_build_system_get_builddir (IdeBuildSystem   *build_system,
+                                         IdeConfiguration *configuration)
+{
+  IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)build_system;
+  g_autoptr(GFile) makefile = NULL;
+  IdeContext *context;
+  IdeVcs *vcs;
+  GFile *workdir;
+
+  g_assert (IDE_IS_AUTOTOOLS_BUILD_SYSTEM (self));
+  g_assert (IDE_IS_CONFIGURATION (configuration));
+
+  /*
+   * If there is a Makefile in the build directory, then the project has been
+   * configured in-tree, and we must override the builddir to perform in-tree
+   * builds.
+   */
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  vcs = ide_context_get_vcs (context);
+  workdir = ide_vcs_get_working_directory (vcs);
+
+  if (!g_file_is_native (workdir))
+    return NULL;
+
+  makefile = g_file_get_child (workdir, "Makefile");
+
+  if (g_file_query_exists (makefile, NULL))
+    return g_file_get_path (workdir);
+
+  return NULL;
+}
+
+static void
 ide_autotools_build_system_finalize (GObject *object)
 {
   IdeAutotoolsBuildSystem *self = (IdeAutotoolsBuildSystem *)object;
@@ -381,7 +684,11 @@ static void
 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_builddir = ide_autotools_build_system_get_builddir;
+  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
diff --git a/plugins/autotools/ide-autotools-makecache-stage.c 
b/plugins/autotools/ide-autotools-makecache-stage.c
new file mode 100644
index 0000000..7e7fe99
--- /dev/null
+++ b/plugins/autotools/ide-autotools-makecache-stage.c
@@ -0,0 +1,253 @@
+/* ide-autotools-makecache-stage.c
+ *
+ * Copyright (C) 2017 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-autotools-makecache-stage"
+
+#include <glib/gi18n.h>
+
+#include "ide-autotools-makecache-stage.h"
+
+struct _IdeAutotoolsMakecacheStage
+{
+  IdeBuildStageLauncher  parent_instance;
+
+  IdeMakecache          *makecache;
+  IdeRuntime            *runtime;
+  GFile                 *cache_file;
+};
+
+G_DEFINE_TYPE (IdeAutotoolsMakecacheStage, ide_autotools_makecache_stage, IDE_TYPE_BUILD_STAGE_LAUNCHER)
+
+static void
+ide_autotools_makecache_stage_makecache_cb (GObject      *object,
+                                            GAsyncResult *result,
+                                            gpointer      user_data)
+{
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(IdeMakecache) makecache = NULL;
+  g_autoptr(GError) error = NULL;
+  IdeAutotoolsMakecacheStage *self;
+
+  IDE_ENTRY;
+
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  makecache = ide_makecache_new_for_cache_file_finish (result, &error);
+
+  if (makecache == NULL)
+    {
+      g_task_return_error (task, g_steal_pointer (&task));
+      IDE_EXIT;
+    }
+
+  self = g_task_get_source_object (task);
+  g_assert (IDE_IS_AUTOTOOLS_MAKECACHE_STAGE (self));
+
+  g_clear_object (&self->makecache);
+  self->makecache = g_steal_pointer (&makecache);
+
+  g_task_return_boolean (task, TRUE);
+
+  IDE_EXIT;
+}
+
+static void
+ide_autotools_makecache_stage_execute_cb (GObject      *object,
+                                          GAsyncResult *result,
+                                          gpointer      user_data)
+{
+  IdeAutotoolsMakecacheStage *self = (IdeAutotoolsMakecacheStage *)object;
+  IdeBuildStage *stage = (IdeBuildStage *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  GCancellable *cancellable;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_AUTOTOOLS_MAKECACHE_STAGE (stage));
+  g_assert (IDE_IS_AUTOTOOLS_MAKECACHE_STAGE (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  if (!IDE_BUILD_STAGE_CLASS (ide_autotools_makecache_stage_parent_class)->execute_finish (stage, result, 
&error))
+    {
+      g_warning ("%s", error->message);
+      g_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  cancellable = g_task_get_cancellable (task);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  /*
+   * Now that we have our makecache file created, we can mmap() it into our
+   * application address space using IdeMakecache.
+   */
+
+  ide_makecache_new_for_cache_file_async (self->runtime,
+                                          self->cache_file,
+                                          cancellable,
+                                          ide_autotools_makecache_stage_makecache_cb,
+                                          g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static void
+ide_autotools_makecache_stage_execute_async (IdeBuildStage       *stage,
+                                             IdeBuildPipeline    *pipeline,
+                                             GCancellable        *cancellable,
+                                             GAsyncReadyCallback  callback,
+                                             gpointer             user_data)
+{
+  IdeAutotoolsMakecacheStage *self = (IdeAutotoolsMakecacheStage *)stage;
+  g_autoptr(GTask) task = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_AUTOTOOLS_MAKECACHE_STAGE (self));
+  g_assert (IDE_IS_BUILD_PIPELINE (pipeline));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_autotools_makecache_stage_execute_async);
+
+  /*
+   * First we need to execute our launcher (performed by our parent class).
+   * Only after that has succeeded to we move on to loading the makecache file
+   * by mmap()'ing the generated make output.
+   */
+
+  IDE_BUILD_STAGE_CLASS (ide_autotools_makecache_stage_parent_class)->execute_async (stage,
+                                                                                     pipeline,
+                                                                                     cancellable,
+                                                                                     
ide_autotools_makecache_stage_execute_cb,
+                                                                                     g_steal_pointer 
(&task));
+
+  IDE_EXIT;
+}
+
+static gboolean
+ide_autotools_makecache_stage_execute_finish (IdeBuildStage  *stage,
+                                              GAsyncResult   *result,
+                                              GError        **error)
+{
+  gboolean ret;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_AUTOTOOLS_MAKECACHE_STAGE (stage));
+  g_assert (G_IS_TASK (result));
+
+  ret = g_task_propagate_boolean (G_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+static void
+ide_autotools_makecache_stage_finalize (GObject *object)
+{
+  IdeAutotoolsMakecacheStage *self = (IdeAutotoolsMakecacheStage *)object;
+
+  IDE_ENTRY;
+
+  g_clear_object (&self->makecache);
+  g_clear_object (&self->cache_file);
+  g_clear_object (&self->runtime);
+
+  G_OBJECT_CLASS (ide_autotools_makecache_stage_parent_class)->finalize (object);
+
+  IDE_EXIT;
+}
+
+static void
+ide_autotools_makecache_stage_class_init (IdeAutotoolsMakecacheStageClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeBuildStageClass *build_stage_class = IDE_BUILD_STAGE_CLASS (klass);
+
+  object_class->finalize = ide_autotools_makecache_stage_finalize;
+
+  build_stage_class->execute_async = ide_autotools_makecache_stage_execute_async;
+  build_stage_class->execute_finish = ide_autotools_makecache_stage_execute_finish;
+}
+
+static void
+ide_autotools_makecache_stage_init (IdeAutotoolsMakecacheStage *self)
+{
+  ide_build_stage_set_name (IDE_BUILD_STAGE (self), _("Building cacheā€¦"));
+}
+
+IdeBuildStage *
+ide_autotools_makecache_stage_new_for_pipeline (IdeBuildPipeline  *pipeline,
+                                                GError           **error)
+{
+  g_autoptr(IdeAutotoolsMakecacheStage) stage = NULL;
+  g_autoptr(GFile) cache_file = NULL;
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autofree gchar *cache_path = NULL;
+  const gchar *make = "make";
+  IdeConfiguration *config;
+  IdeRuntime *runtime;
+  IdeContext *context;
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (IDE_IS_BUILD_PIPELINE (pipeline), NULL);
+
+  context = ide_object_get_context (IDE_OBJECT (pipeline));
+  config = ide_build_pipeline_get_configuration (pipeline);
+  runtime = ide_configuration_get_runtime (config);
+
+  cache_path = ide_build_pipeline_build_builddir_path (pipeline, "Makecache", NULL);
+
+  if (ide_runtime_contains_program_in_path (runtime, "gmake", NULL))
+    make = "gmake";
+
+  if (NULL == (launcher = ide_build_pipeline_create_launcher (pipeline, error)))
+    IDE_RETURN (NULL);
+
+  ide_subprocess_launcher_push_argv (launcher, make);
+  ide_subprocess_launcher_push_argv (launcher, "-p");
+  ide_subprocess_launcher_push_argv (launcher, "-n");
+  ide_subprocess_launcher_push_argv (launcher, "-s");
+
+  stage = g_object_new (IDE_TYPE_AUTOTOOLS_MAKECACHE_STAGE,
+                        "context", context,
+                        "launcher", launcher,
+                        "ignore-exit-status", TRUE,
+                        NULL);
+
+  ide_build_stage_set_stdout_path (IDE_BUILD_STAGE (stage), cache_path);
+
+  g_assert_cmpint (ide_build_stage_launcher_get_ignore_exit_status (IDE_BUILD_STAGE_LAUNCHER (stage)), ==, 
TRUE);
+
+  stage->runtime = g_object_ref (runtime);
+  stage->cache_file = g_file_new_for_path (cache_path);
+
+  IDE_RETURN (g_steal_pointer (&stage));
+}
+
+IdeMakecache *
+ide_autotools_makecache_stage_get_makecache (IdeAutotoolsMakecacheStage *self)
+{
+  g_return_val_if_fail (IDE_IS_AUTOTOOLS_MAKECACHE_STAGE (self), NULL);
+
+  return self->makecache;
+}
diff --git a/plugins/autotools/ide-autotools-makecache-stage.h 
b/plugins/autotools/ide-autotools-makecache-stage.h
new file mode 100644
index 0000000..97bd147
--- /dev/null
+++ b/plugins/autotools/ide-autotools-makecache-stage.h
@@ -0,0 +1,38 @@
+/* ide-autotools-makecache-stage.h
+ *
+ * Copyright (C) 2017 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_AUTOTOOLS_MAKECACHE_STAGE_H
+#define IDE_AUTOTOOLS_MAKECACHE_STAGE_H
+
+#include <ide.h>
+
+#include "ide-makecache.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_AUTOTOOLS_MAKECACHE_STAGE (ide_autotools_makecache_stage_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeAutotoolsMakecacheStage, ide_autotools_makecache_stage, IDE, 
AUTOTOOLS_MAKECACHE_STAGE, IdeBuildStageLauncher)
+
+IdeBuildStage *ide_autotools_makecache_stage_new_for_pipeline (IdeBuildPipeline            *pipeline,
+                                                               GError                     **error);
+IdeMakecache  *ide_autotools_makecache_stage_get_makecache    (IdeAutotoolsMakecacheStage  *self);
+
+G_END_DECLS
+
+#endif /* IDE_AUTOTOOLS_MAKECACHE_STAGE_H */
diff --git a/plugins/autotools/ide-autotools-pipeline-addin.c 
b/plugins/autotools/ide-autotools-pipeline-addin.c
new file mode 100644
index 0000000..3f2ac38
--- /dev/null
+++ b/plugins/autotools/ide-autotools-pipeline-addin.c
@@ -0,0 +1,427 @@
+/* ide-autotools-pipeline-addin.c
+ *
+ * Copyright (C) 2016 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-autotools-pipeline-addin"
+
+#include "ide-autotools-autogen-stage.h"
+#include "ide-autotools-build-system.h"
+#include "ide-autotools-makecache-stage.h"
+#include "ide-autotools-pipeline-addin.h"
+
+static gboolean
+register_autoreconf_stage (IdeAutotoolsPipelineAddin  *self,
+                           IdeBuildPipeline           *pipeline,
+                           GError                    **error)
+{
+  g_autofree gchar *configure_path = NULL;
+  g_autoptr(IdeBuildStage) stage = NULL;
+  IdeContext *context;
+  const gchar *srcdir;
+  gboolean completed;
+  guint stage_id;
+
+  g_assert (IDE_IS_AUTOTOOLS_PIPELINE_ADDIN (self));
+  g_assert (IDE_IS_BUILD_PIPELINE (pipeline));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  configure_path = ide_build_pipeline_build_srcdir_path (pipeline, "configure", NULL);
+  completed = g_file_test (configure_path, G_FILE_TEST_IS_REGULAR);
+  srcdir = ide_build_pipeline_get_srcdir (pipeline);
+
+  stage = g_object_new (IDE_TYPE_AUTOTOOLS_AUTOGEN_STAGE,
+                        "completed", completed,
+                        "context", context,
+                        "srcdir", srcdir,
+                        NULL);
+
+  stage_id = ide_build_pipeline_connect (pipeline, IDE_BUILD_PHASE_AUTOGEN, 0, stage);
+
+  ide_build_pipeline_addin_track (IDE_BUILD_PIPELINE_ADDIN (self), stage_id);
+
+  return TRUE;
+}
+
+static gint
+compare_mtime (const gchar *path_a,
+               const gchar *path_b)
+{
+  g_autoptr(GFile) file_a = g_file_new_for_path (path_a);
+  g_autoptr(GFile) file_b = g_file_new_for_path (path_b);
+  g_autoptr(GFileInfo) info_a = NULL;
+  g_autoptr(GFileInfo) info_b = NULL;
+  gint64 ret = 0;
+
+  info_a = g_file_query_info (file_a,
+                              G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                              G_FILE_QUERY_INFO_NONE,
+                              NULL,
+                              NULL);
+
+  info_b = g_file_query_info (file_b,
+                              G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                              G_FILE_QUERY_INFO_NONE,
+                              NULL,
+                              NULL);
+
+  ret = (gint64)g_file_info_get_attribute_uint64 (info_a, G_FILE_ATTRIBUTE_TIME_MODIFIED) -
+        (gint64)g_file_info_get_attribute_uint64 (info_b, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+  if (ret < 0)
+    return -1;
+  else if (ret > 0)
+    return 1;
+  return 0;
+}
+
+static void
+check_configure_status (IdeAutotoolsPipelineAddin *self,
+                        IdeBuildPipeline          *pipeline,
+                        GCancellable              *cancellable,
+                        IdeBuildStage             *stage)
+{
+  g_autofree gchar *configure_ac = NULL;
+  g_autofree gchar *configure = NULL;
+  g_autofree gchar *config_status = NULL;
+  g_autofree gchar *makefile = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_AUTOTOOLS_PIPELINE_ADDIN (self));
+  g_assert (IDE_IS_BUILD_PIPELINE (pipeline));
+  g_assert (IDE_IS_BUILD_STAGE (stage));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  configure = ide_build_pipeline_build_srcdir_path (pipeline, "configure", NULL);
+  configure_ac = ide_build_pipeline_build_srcdir_path (pipeline, "configure.ac", NULL);
+  config_status = ide_build_pipeline_build_builddir_path (pipeline, "config.status", NULL);
+  makefile = ide_build_pipeline_build_builddir_path (pipeline, "Makefile", NULL);
+
+  IDE_TRACE_MSG (" configure.ac is at %s", configure_ac);
+  IDE_TRACE_MSG (" configure is at %s", configure);
+  IDE_TRACE_MSG (" config.status is at %s", config_status);
+  IDE_TRACE_MSG (" makefile is at %s", makefile);
+
+  /*
+   * First make sure some essential files exist. If not, we need to run the
+   * configure process.
+   *
+   * TODO: This may take some tweaking if we ever try to reuse existing builds
+   *       that were performed in-tree.
+   */
+  if (!g_file_test (configure_ac, G_FILE_TEST_IS_REGULAR) ||
+      !g_file_test (configure, G_FILE_TEST_IS_REGULAR) ||
+      !g_file_test (config_status, G_FILE_TEST_IS_REGULAR) ||
+      !g_file_test (makefile, G_FILE_TEST_IS_REGULAR))
+    {
+      ide_build_stage_set_completed (stage, FALSE);
+      IDE_EXIT;
+    }
+
+  /*
+   * Now make sure that config.status and Makefile are indeed newer than
+   * our configure script.
+   */
+  if (compare_mtime (configure_ac, configure) < 0 &&
+      compare_mtime (configure, config_status) < 0 &&
+      compare_mtime (configure, makefile) < 0)
+    {
+      /*
+       * TODO: It would be fancy if we could look at '^ac_cs_config=' to determine
+       * if the configure args match what we expect. But this is a bit more
+       * complicated than simply a string comparison.
+       */
+      ide_build_stage_set_completed (stage, TRUE);
+      IDE_EXIT;
+    }
+
+  ide_build_stage_set_completed (stage, FALSE);
+
+  IDE_EXIT;
+}
+
+static gboolean
+register_configure_stage (IdeAutotoolsPipelineAddin  *self,
+                          IdeBuildPipeline           *pipeline,
+                          GError                    **error)
+{
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autoptr(IdeBuildStage) stage = NULL;
+  IdeConfiguration *configuration;
+  g_autofree gchar *configure_path = NULL;
+  const gchar *config_opts;
+  const gchar *prefix;
+  guint stage_id;
+
+  g_assert (IDE_IS_AUTOTOOLS_PIPELINE_ADDIN (self));
+  g_assert (IDE_IS_BUILD_PIPELINE (pipeline));
+
+  launcher = ide_build_pipeline_create_launcher (pipeline, error);
+
+  if (launcher == NULL)
+    return FALSE;
+
+  configure_path = ide_build_pipeline_build_srcdir_path (pipeline, "configure", NULL);
+  ide_subprocess_launcher_push_argv (launcher, configure_path);
+
+  /*
+   * Parse the configure options as defined in the build configuration and append
+   * them to configure.
+   */
+
+  configuration = ide_build_pipeline_get_configuration (pipeline);
+  config_opts = ide_configuration_get_config_opts (configuration);
+  prefix = ide_configuration_get_prefix (configuration);
+
+  if (prefix != NULL)
+    {
+      g_autofree gchar *prefix_arg = g_strdup_printf ("--prefix=%s", prefix);
+      ide_subprocess_launcher_push_argv (launcher, prefix_arg);
+    }
+
+  if (!ide_str_empty0 (config_opts))
+    {
+      g_auto(GStrv) argv = NULL;
+      gint argc = 0;
+
+      if (!g_shell_parse_argv (config_opts, &argc, &argv, error))
+        return FALSE;
+
+      for (gint i = 0; i < argc; i++)
+        ide_subprocess_launcher_push_argv (launcher, argv[i]);
+    }
+
+  stage = g_object_new (IDE_TYPE_BUILD_STAGE_LAUNCHER,
+                        "context", ide_object_get_context (IDE_OBJECT (self)),
+                        "launcher", launcher,
+                        NULL);
+
+  /*
+   * If the Makefile exists within the builddir, we will assume the
+   * project has been initially configured correctly. Otherwise, every
+   * time the user opens the project they have to go through a full
+   * re-configure and build.
+   *
+   * Should the user need to perform an autogen, a manual rebuild is
+   * easily achieved so this seems to be the sensible default.
+   *
+   * If we were to do this "correctly", we would look at config.status to
+   * match the "ac_cs_config" variable to what we set. However, that is
+   * influenced by environment variables, so its a bit non-trivial.
+   */
+  g_signal_connect_object (stage,
+                           "query",
+                           G_CALLBACK (check_configure_status),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  stage_id = ide_build_pipeline_connect (pipeline, IDE_BUILD_PHASE_CONFIGURE, 0, stage);
+
+  ide_build_pipeline_addin_track (IDE_BUILD_PIPELINE_ADDIN (self), stage_id);
+
+  return TRUE;
+}
+
+static void
+make_stage_query (IdeAutotoolsPipelineAddin *self,
+                  IdeBuildPipeline          *pipeline,
+                  GCancellable              *cancellable,
+                  IdeBuildStageLauncher     *stage)
+{
+  g_assert (IDE_IS_AUTOTOOLS_PIPELINE_ADDIN (self));
+  g_assert (IDE_IS_BUILD_PIPELINE (pipeline));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_assert (IDE_IS_BUILD_STAGE_LAUNCHER (stage));
+
+  /* Always rely on make to determine up-to-date status */
+  ide_build_stage_set_completed (IDE_BUILD_STAGE (stage), FALSE);
+}
+
+G_GNUC_NULL_TERMINATED static gboolean
+register_make_stage (IdeAutotoolsPipelineAddin  *self,
+                     IdeBuildPipeline           *pipeline,
+                     IdeRuntime                 *runtime,
+                     const gchar                *log_file,
+                     gboolean                    ignore_exit_code,
+                     gboolean                    include_clean,
+                     IdeBuildPhase               phase,
+                     GError                    **error,
+                     const gchar                *make,
+                     const gchar                *first_target,
+                     ...)
+{
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autoptr(IdeBuildStage) stage = NULL;
+  IdeConfiguration *configuration;
+  g_autofree gchar *j = NULL;
+  IdeContext *context;
+  guint stage_id;
+  gint parallel;
+  va_list args;
+
+  g_assert (IDE_IS_AUTOTOOLS_PIPELINE_ADDIN (self));
+  g_assert (IDE_IS_BUILD_PIPELINE (pipeline));
+  g_assert (IDE_IS_RUNTIME (runtime));
+
+  context = ide_object_get_context (IDE_OBJECT (pipeline));
+  configuration = ide_build_pipeline_get_configuration (pipeline);
+
+  launcher = ide_build_pipeline_create_launcher (pipeline, error);
+
+  if (launcher == NULL)
+    return FALSE;
+
+  parallel = ide_configuration_get_parallelism (configuration);
+
+  if (parallel == -1)
+    j = g_strdup_printf ("-j%u", g_get_num_processors () + 1);
+  else if (parallel == 0)
+    j = g_strdup_printf ("-j%u", g_get_num_processors ());
+  else
+    j = g_strdup_printf ("-j%u", parallel);
+
+  ide_subprocess_launcher_push_argv (launcher, make);
+  ide_subprocess_launcher_push_argv (launcher, j);
+
+  /* We want silent rules when possible */
+  ide_subprocess_launcher_push_argv (launcher, "V=0");
+
+  va_start (args, first_target);
+  do
+    ide_subprocess_launcher_push_argv (launcher, first_target);
+  while (NULL != (first_target = va_arg (args, const gchar *)));
+  va_end (args);
+
+  stage = ide_build_stage_launcher_new (context, launcher);
+
+  if (log_file != NULL)
+    ide_build_stage_set_stdout_path (stage, log_file);
+
+  if (ignore_exit_code)
+    ide_build_stage_launcher_set_ignore_exit_status (IDE_BUILD_STAGE_LAUNCHER (stage), TRUE);
+
+  g_signal_connect_object (stage,
+                           "query",
+                           G_CALLBACK (make_stage_query),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  if (include_clean)
+    {
+      g_autoptr(IdeSubprocessLauncher) clean_launcher = NULL;
+
+      clean_launcher = ide_build_pipeline_create_launcher (pipeline, error);
+
+      if (clean_launcher == NULL)
+        return FALSE;
+
+      ide_subprocess_launcher_push_argv (clean_launcher, make);
+      ide_subprocess_launcher_push_argv (clean_launcher, "clean");
+
+      ide_build_stage_launcher_set_clean_launcher (IDE_BUILD_STAGE_LAUNCHER (stage), clean_launcher);
+    }
+
+  stage_id = ide_build_pipeline_connect (pipeline, phase, 0, stage);
+
+  ide_build_pipeline_addin_track (IDE_BUILD_PIPELINE_ADDIN (self), stage_id);
+
+  return TRUE;
+}
+
+static gboolean
+register_makecache_stage (IdeAutotoolsPipelineAddin  *self,
+                          IdeBuildPipeline           *pipeline,
+                          GError                    **error)
+{
+  g_autoptr(IdeBuildStage) stage = NULL;
+  guint stage_id;
+
+  g_assert (IDE_IS_AUTOTOOLS_PIPELINE_ADDIN (self));
+  g_assert (IDE_IS_BUILD_PIPELINE (pipeline));
+
+  if (NULL == (stage = ide_autotools_makecache_stage_new_for_pipeline (pipeline, error)))
+    return FALSE;
+
+  stage_id = ide_build_pipeline_connect (pipeline,
+                                         IDE_BUILD_PHASE_CONFIGURE | IDE_BUILD_PHASE_AFTER,
+                                         0,
+                                         stage);
+  ide_build_pipeline_addin_track (IDE_BUILD_PIPELINE_ADDIN (self), stage_id);
+
+  return TRUE;
+}
+
+static void
+ide_autotools_pipeline_addin_load (IdeBuildPipelineAddin *addin,
+                                   IdeBuildPipeline      *pipeline)
+{
+  IdeAutotoolsPipelineAddin *self = (IdeAutotoolsPipelineAddin *)addin;
+  g_autoptr(GError) error = NULL;
+  IdeConfiguration *config;
+  IdeBuildSystem *build_system;
+  IdeContext *context;
+  IdeRuntime *runtime;
+  const gchar *make = "make";
+
+  g_assert (IDE_IS_AUTOTOOLS_PIPELINE_ADDIN (self));
+  g_assert (IDE_IS_BUILD_PIPELINE (pipeline));
+
+  context = ide_object_get_context (IDE_OBJECT (addin));
+  build_system = ide_context_get_build_system (context);
+  config = ide_build_pipeline_get_configuration (pipeline);
+  runtime = ide_configuration_get_runtime (config);
+
+  if (!IDE_IS_AUTOTOOLS_BUILD_SYSTEM (build_system))
+    return;
+
+  if (ide_runtime_contains_program_in_path (runtime, "gmake", NULL))
+    make = "gmake";
+
+  if (!register_autoreconf_stage (self, pipeline, &error) ||
+      !register_configure_stage (self, pipeline, &error) ||
+      !register_makecache_stage (self, pipeline, &error) ||
+      !register_make_stage (self, pipeline, runtime, NULL, FALSE, TRUE, IDE_BUILD_PHASE_BUILD, &error, make, 
"all", NULL) ||
+      !register_make_stage (self, pipeline, runtime, NULL, FALSE, FALSE, IDE_BUILD_PHASE_INSTALL, &error, 
make, "install", NULL))
+    {
+      g_assert (error != NULL);
+      g_warning ("Failed to create autotools launcher: %s", error->message);
+      return;
+    }
+}
+
+/* GObject Boilerplate */
+
+static void
+addin_iface_init (IdeBuildPipelineAddinInterface *iface)
+{
+  iface->load = ide_autotools_pipeline_addin_load;
+}
+
+struct _IdeAutotoolsPipelineAddin { IdeObject parent; };
+
+G_DEFINE_TYPE_WITH_CODE (IdeAutotoolsPipelineAddin, ide_autotools_pipeline_addin, IDE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_BUILD_PIPELINE_ADDIN, addin_iface_init))
+
+static void
+ide_autotools_pipeline_addin_class_init (IdeAutotoolsPipelineAddinClass *klass)
+{
+}
+
+static void
+ide_autotools_pipeline_addin_init (IdeAutotoolsPipelineAddin *self)
+{
+}
diff --git a/plugins/autotools/ide-autotools-builder.h b/plugins/autotools/ide-autotools-pipeline-addin.h
similarity index 57%
rename from plugins/autotools/ide-autotools-builder.h
rename to plugins/autotools/ide-autotools-pipeline-addin.h
index 28be301..d3535f3 100644
--- a/plugins/autotools/ide-autotools-builder.h
+++ b/plugins/autotools/ide-autotools-pipeline-addin.h
@@ -1,6 +1,6 @@
-/* ide-autotools-builder.h
+/* ide-autotools-pipeline-addin.h
  *
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * 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
@@ -16,20 +16,17 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef IDE_AUTOTOOLS_BUILDER_H
-#define IDE_AUTOTOOLS_BUILDER_H
+#ifndef IDE_AUTOTOOLS_PIPELINE_ADDIN_H
+#define IDE_AUTOTOOLS_PIPELINE_ADDIN_H
 
 #include <ide.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_AUTOTOOLS_BUILDER (ide_autotools_builder_get_type())
+#define IDE_TYPE_AUTOTOOLS_PIPELINE_ADDIN (ide_autotools_pipeline_addin_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeAutotoolsBuilder, ide_autotools_builder, IDE, AUTOTOOLS_BUILDER, IdeBuilder)
-
-GFile    *ide_autotools_builder_get_build_directory (IdeAutotoolsBuilder  *self);
-gboolean  ide_autotools_builder_get_needs_bootstrap (IdeAutotoolsBuilder  *self);
+G_DECLARE_FINAL_TYPE (IdeAutotoolsPipelineAddin, ide_autotools_pipeline_addin, IDE, 
AUTOTOOLS_PIPELINE_ADDIN, IdeObject)
 
 G_END_DECLS
 
-#endif /* IDE_AUTOTOOLS_BUILDER_H */
+#endif /* IDE_AUTOTOOLS_PIPELINE_ADDIN_H */


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