[gnome-builder] autotools: add IdeBuildTarget and install support to autotools builder



commit 2573349b73e3d2ce90ee1e0caf7e70d9b4860954
Author: Christian Hergert <chergert redhat com>
Date:   Sun Jul 17 03:14:00 2016 -0700

    autotools: add IdeBuildTarget and install support to autotools builder

 plugins/autotools/Makefile.am                  |    2 +
 plugins/autotools/ide-autotools-build-system.c |   90 ++++++
 plugins/autotools/ide-autotools-build-target.c |  166 ++++++++++
 plugins/autotools/ide-autotools-build-target.h |   32 ++
 plugins/autotools/ide-autotools-build-task.c   |   72 ++++-
 plugins/autotools/ide-autotools-build-task.h   |    3 +-
 plugins/autotools/ide-autotools-builder.c      |   89 ++++++-
 plugins/autotools/ide-makecache.c              |  391 +++++++++++++++++++++++-
 plugins/autotools/ide-makecache.h              |   55 ++--
 9 files changed, 861 insertions(+), 39 deletions(-)
---
diff --git a/plugins/autotools/Makefile.am b/plugins/autotools/Makefile.am
index 195a390..24481ad 100644
--- a/plugins/autotools/Makefile.am
+++ b/plugins/autotools/Makefile.am
@@ -10,6 +10,8 @@ libautotools_plugin_la_SOURCES = \
        ide-autotools-builder.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-project-miner.c \
diff --git a/plugins/autotools/ide-autotools-build-system.c b/plugins/autotools/ide-autotools-build-system.c
index 12c9f97..9eb1807 100644
--- a/plugins/autotools/ide-autotools-build-system.c
+++ b/plugins/autotools/ide-autotools-build-system.c
@@ -546,6 +546,94 @@ 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;
+  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;
+    }
+
+  ide_makecache_get_build_targets_async (makecache,
+                                         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)
 {
@@ -614,6 +702,8 @@ build_system_iface_init (IdeBuildSystemInterface *iface)
   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
diff --git a/plugins/autotools/ide-autotools-build-target.c b/plugins/autotools/ide-autotools-build-target.c
new file mode 100644
index 0000000..faa3a7d
--- /dev/null
+++ b/plugins/autotools/ide-autotools-build-target.c
@@ -0,0 +1,166 @@
+/* ide-autotools-build-target.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-build-target"
+
+#include "ide-autotools-build-target.h"
+
+struct _IdeAutotoolsBuildTarget
+{
+  IdeObject parent_instance;
+
+  GFile *build_directory;
+  GFile *install_directory;
+  gchar *name;
+};
+
+enum {
+  PROP_0,
+  PROP_BUILD_DIRECTORY,
+  PROP_INSTALL_DIRECTORY,
+  PROP_NAME,
+  N_PROPS
+};
+
+static void build_target_iface_init (IdeBuildTargetInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (IdeAutotoolsBuildTarget, ide_autotools_build_target, IDE_TYPE_OBJECT, 0,
+                        G_IMPLEMENT_INTERFACE (IDE_TYPE_BUILD_TARGET, build_target_iface_init))
+
+static GParamSpec *properties [N_PROPS];
+
+static void
+ide_autotools_build_target_finalize (GObject *object)
+{
+  IdeAutotoolsBuildTarget *self = (IdeAutotoolsBuildTarget *)object;
+
+  g_clear_object (&self->build_directory);
+  g_clear_object (&self->install_directory);
+  g_clear_pointer (&self->name, g_free);
+
+  G_OBJECT_CLASS (ide_autotools_build_target_parent_class)->finalize (object);
+}
+
+static void
+ide_autotools_build_target_get_property (GObject    *object,
+                                         guint       prop_id,
+                                         GValue     *value,
+                                         GParamSpec *pspec)
+{
+  IdeAutotoolsBuildTarget *self = IDE_AUTOTOOLS_BUILD_TARGET (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUILD_DIRECTORY:
+      g_value_set_object (value, self->build_directory);
+      break;
+
+    case PROP_INSTALL_DIRECTORY:
+      g_value_set_object (value, self->install_directory);
+      break;
+
+    case PROP_NAME:
+      g_value_set_string (value, self->name);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_autotools_build_target_set_property (GObject      *object,
+                                         guint         prop_id,
+                                         const GValue *value,
+                                         GParamSpec   *pspec)
+{
+  IdeAutotoolsBuildTarget *self = IDE_AUTOTOOLS_BUILD_TARGET (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUILD_DIRECTORY:
+      self->build_directory = g_value_dup_object (value);
+      break;
+
+    case PROP_INSTALL_DIRECTORY:
+      self->install_directory = g_value_dup_object (value);
+      break;
+
+    case PROP_NAME:
+      self->name = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_autotools_build_target_class_init (IdeAutotoolsBuildTargetClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_autotools_build_target_finalize;
+  object_class->get_property = ide_autotools_build_target_get_property;
+  object_class->set_property = ide_autotools_build_target_set_property;
+
+  properties [PROP_BUILD_DIRECTORY] =
+    g_param_spec_object ("build-directory",
+                         NULL,
+                         NULL,
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_INSTALL_DIRECTORY] =
+    g_param_spec_object ("install-directory",
+                         NULL,
+                         NULL,
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_NAME] =
+    g_param_spec_string ("name",
+                         NULL,
+                         NULL,
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_autotools_build_target_init (IdeAutotoolsBuildTarget *self)
+{
+}
+
+static GFile *
+ide_autotools_build_target_get_install_directory (IdeBuildTarget *target)
+{
+  IdeAutotoolsBuildTarget *self = (IdeAutotoolsBuildTarget *)target;
+
+  if (self->install_directory != NULL)
+    return g_object_ref (self->install_directory);
+
+  return NULL;
+}
+
+static void
+build_target_iface_init (IdeBuildTargetInterface *iface)
+{
+  iface->get_install_directory = ide_autotools_build_target_get_install_directory;
+}
diff --git a/plugins/autotools/ide-autotools-build-target.h b/plugins/autotools/ide-autotools-build-target.h
new file mode 100644
index 0000000..986e17a
--- /dev/null
+++ b/plugins/autotools/ide-autotools-build-target.h
@@ -0,0 +1,32 @@
+/* ide-autotools-build-target.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_AUTOTOOLS_BUILD_TARGET_H
+#define IDE_AUTOTOOLS_BUILD_TARGET_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_AUTOTOOLS_BUILD_TARGET (ide_autotools_build_target_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeAutotoolsBuildTarget, ide_autotools_build_target, IDE, AUTOTOOLS_BUILD_TARGET, 
IdeObject)
+
+G_END_DECLS
+
+#endif /* IDE_AUTOTOOLS_BUILD_TARGET_H */
diff --git a/plugins/autotools/ide-autotools-build-task.c b/plugins/autotools/ide-autotools-build-task.c
index 062700b..f63634d 100644
--- a/plugins/autotools/ide-autotools-build-task.c
+++ b/plugins/autotools/ide-autotools-build-task.c
@@ -217,6 +217,7 @@ ide_autotools_build_task_finalize (GObject *object)
 
   g_clear_object (&self->directory);
   g_clear_object (&self->configuration);
+  g_clear_pointer (&self->extra_targets, g_ptr_array_unref);
 
   G_OBJECT_CLASS (ide_autotools_build_task_parent_class)->finalize (object);
 }
@@ -394,8 +395,9 @@ gen_configure_argv (IdeAutotoolsBuildTask *self,
 }
 
 static WorkerState *
-worker_state_new (IdeAutotoolsBuildTask *self,
-                  IdeBuilderBuildFlags   flags)
+worker_state_new (IdeAutotoolsBuildTask  *self,
+                  IdeBuilderBuildFlags    flags,
+                  GError                **error)
 {
   g_autofree gchar *name = NULL;
   IdeContext *context;
@@ -416,6 +418,28 @@ worker_state_new (IdeAutotoolsBuildTask *self,
   device = ide_configuration_get_device (self->configuration);
   runtime = ide_configuration_get_runtime (self->configuration);
 
+  if (device == NULL)
+    {
+      g_set_error (error,
+                   IDE_DEVICE_ERROR,
+                   IDE_DEVICE_ERROR_NO_SUCH_DEVICE,
+                   "%s “%s”",
+                   _("Failed to locate device"),
+                   ide_configuration_get_device_id (self->configuration));
+      return NULL;
+    }
+
+  if (runtime == NULL)
+    {
+      g_set_error (error,
+                   IDE_RUNTIME_ERROR,
+                   IDE_RUNTIME_ERROR_NO_SUCH_RUNTIME,
+                   "%s “%s”",
+                   _("Failed to locate runtime"),
+                   ide_configuration_get_runtime_id (self->configuration));
+      return NULL;
+    }
+
   name = g_file_get_basename (project_file);
 
   if (g_str_has_prefix (name, "configure."))
@@ -456,6 +480,16 @@ worker_state_new (IdeAutotoolsBuildTask *self,
   if (FLAG_UNSET (flags, IDE_BUILDER_BUILD_FLAGS_NO_BUILD))
     g_ptr_array_add (make_targets, g_strdup ("all"));
 
+  if (self->extra_targets != NULL)
+    {
+      for (guint i = 0; i < self->extra_targets->len; i++)
+        {
+          const gchar *target = g_ptr_array_index (self->extra_targets, i);
+
+          g_ptr_array_add (make_targets, g_strdup (target));
+        }
+    }
+
   g_ptr_array_add (make_targets, NULL);
 
   state->make_targets = (gchar **)g_ptr_array_free (make_targets, FALSE);
@@ -543,25 +577,34 @@ ide_autotools_build_task_execute_async (IdeAutotoolsBuildTask *self,
 {
   g_autoptr(GTask) task = NULL;
   WorkerState *state;
+  GError *error = NULL;
 
   g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_autotools_build_task_execute_async);
+
   if (self->executed)
     {
-      g_task_report_new_error (self, callback, user_data,
-                               ide_autotools_build_task_execute_async,
+      g_task_return_new_error (task,
                                G_IO_ERROR,
                                G_IO_ERROR_FAILED,
-                               _("Cannot execute build task more than once."));
+                               "%s",
+                               _("Cannot execute build task more than once"));
       return;
     }
 
   self->executed = TRUE;
 
-  state = worker_state_new (self, flags);
+  state = worker_state_new (self, flags, &error);
+
+  if (state == NULL)
+    {
+      g_task_return_error (task, error);
+      return;
+    }
 
-  task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_task_data (task, state, worker_state_free);
 
   /*
@@ -598,6 +641,8 @@ ide_autotools_build_task_execute_finish (IdeAutotoolsBuildTask  *self,
   if (ret == FALSE)
     ide_build_result_set_failed (IDE_BUILD_RESULT (self), TRUE);
 
+  ide_build_result_set_running (IDE_BUILD_RESULT (self), FALSE);
+
   return ret;
 }
 
@@ -952,3 +997,16 @@ apply_environment (IdeAutotoolsBuildTask *self,
   environment = ide_configuration_get_environment (self->configuration);
   ide_subprocess_launcher_overlay_environment (launcher, environment);
 }
+
+void
+ide_autotools_build_task_add_target (IdeAutotoolsBuildTask *self,
+                                     const gchar           *target)
+{
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (self));
+  g_return_if_fail (target != NULL);
+
+  if (self->extra_targets == NULL)
+    self->extra_targets = g_ptr_array_new_with_free_func (g_free);
+
+  g_ptr_array_add (self->extra_targets, g_strdup (target));
+}
diff --git a/plugins/autotools/ide-autotools-build-task.h b/plugins/autotools/ide-autotools-build-task.h
index f9ac97d..3bc8a33 100644
--- a/plugins/autotools/ide-autotools-build-task.h
+++ b/plugins/autotools/ide-autotools-build-task.h
@@ -29,7 +29,8 @@ G_BEGIN_DECLS
 G_DECLARE_FINAL_TYPE (IdeAutotoolsBuildTask, ide_autotools_build_task, IDE, AUTOTOOLS_BUILD_TASK, 
IdeBuildResult)
 
 GFile    *ide_autotools_build_task_get_directory  (IdeAutotoolsBuildTask  *self);
-void      ide_autotools_build_task_add_target     (IdeAutotoolsBuildTask  *self);
+void      ide_autotools_build_task_add_target     (IdeAutotoolsBuildTask  *self,
+                                                   const gchar            *target);
 void      ide_autotools_build_task_execute_async  (IdeAutotoolsBuildTask  *self,
                                                    IdeBuilderBuildFlags    flags,
                                                    GCancellable           *cancellable,
diff --git a/plugins/autotools/ide-autotools-builder.c b/plugins/autotools/ide-autotools-builder.c
index 2d86554..b6d0a41 100644
--- a/plugins/autotools/ide-autotools-builder.c
+++ b/plugins/autotools/ide-autotools-builder.c
@@ -41,8 +41,6 @@ ide_autotools_builder_build_cb (GObject      *object,
   g_return_if_fail (IDE_IS_AUTOTOOLS_BUILD_TASK (build_result));
   g_return_if_fail (G_IS_TASK (task));
 
-  ide_build_result_set_running (IDE_BUILD_RESULT (build_result), FALSE);
-
   if (!ide_autotools_build_task_execute_finish (build_result, result, &error))
     {
       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
@@ -162,7 +160,7 @@ ide_autotools_builder_build_async (IdeBuilder           *builder,
                                "running", TRUE,
                                NULL);
 
-  if (result)
+  if (result != NULL)
     *result = g_object_ref (build_result);
 
   ide_autotools_build_task_execute_async (build_result,
@@ -186,12 +184,97 @@ ide_autotools_builder_build_finish (IdeBuilder    *builder,
 }
 
 static void
+ide_autotools_builder_install_cb (GObject      *object,
+                                  GAsyncResult *result,
+                                  gpointer      user_data)
+{
+  IdeAutotoolsBuildTask *build_task = (IdeAutotoolsBuildTask *)object;
+  g_autoptr(GTask) task = user_data;
+  GError *error = NULL;
+
+  g_assert (IDE_IS_AUTOTOOLS_BUILD_TASK (build_task));
+  g_assert (G_IS_TASK (task));
+
+  if (!ide_autotools_build_task_execute_finish (build_task, result, &error))
+    {
+      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        ide_build_result_set_mode (IDE_BUILD_RESULT (build_task), _("Install cancelled"));
+      else
+        ide_build_result_set_mode (IDE_BUILD_RESULT (build_task), _("Install failed"));
+
+      g_task_return_error (task, error);
+      return;
+    }
+
+  ide_build_result_set_mode (IDE_BUILD_RESULT (build_task), _("Install successful"));
+
+  g_task_return_pointer (task, g_object_ref (build_task), g_object_unref);
+}
+
+static void
+ide_autotools_builder_install_async (IdeBuilder           *builder,
+                                     IdeBuildResult      **result,
+                                     GCancellable         *cancellable,
+                                     GAsyncReadyCallback   callback,
+                                     gpointer              user_data)
+{
+  IdeAutotoolsBuilder *self = (IdeAutotoolsBuilder *)builder;
+  g_autoptr(IdeAutotoolsBuildTask) build_result = NULL;
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GFile) directory = NULL;
+  IdeConfiguration *configuration;
+  IdeContext *context;
+
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILDER (builder));
+  g_return_if_fail (IDE_IS_AUTOTOOLS_BUILDER (self));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+
+  context = ide_object_get_context (IDE_OBJECT (builder));
+  configuration = ide_builder_get_configuration (IDE_BUILDER (self));
+  directory = ide_autotools_builder_get_build_directory (self);
+  build_result = g_object_new (IDE_TYPE_AUTOTOOLS_BUILD_TASK,
+                               "context", context,
+                               "configuration", configuration,
+                               "directory", directory,
+                               "mode", _("Building…"),
+                               "running", TRUE,
+                               NULL);
+
+  ide_autotools_build_task_add_target (build_result, "install");
+
+  if (result != NULL)
+    *result = g_object_ref (build_result);
+
+  ide_autotools_build_task_execute_async (build_result,
+                                          IDE_BUILDER_BUILD_FLAGS_NONE,
+                                          cancellable,
+                                          ide_autotools_builder_install_cb,
+                                          g_object_ref (task));
+}
+
+static IdeBuildResult *
+ide_autotools_builder_install_finish (IdeBuilder    *builder,
+                                      GAsyncResult  *result,
+                                      GError       **error)
+{
+  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);
+
+  return g_task_propagate_pointer (task, error);
+}
+
+static void
 ide_autotools_builder_class_init (IdeAutotoolsBuilderClass *klass)
 {
   IdeBuilderClass *builder_class = IDE_BUILDER_CLASS (klass);
 
   builder_class->build_async = ide_autotools_builder_build_async;
   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;
 }
 
 static void
diff --git a/plugins/autotools/ide-makecache.c b/plugins/autotools/ide-makecache.c
index 95cf869..9a61c62 100644
--- a/plugins/autotools/ide-makecache.c
+++ b/plugins/autotools/ide-makecache.c
@@ -35,16 +35,18 @@
 #include <unistd.h>
 #include <ide.h>
 
+#include "ide-autotools-build-target.h"
 #include "ide-makecache.h"
 #include "ide-makecache-target.h"
 
-#define FAKE_CC    "__LIBIDE_FAKE_CC__"
-#define FAKE_CXX   "__LIBIDE_FAKE_CXX__"
-#define FAKE_VALAC "__LIBIDE_FAKE_VALAC__"
+#define FAKE_CC      "__LIBIDE_FAKE_CC__"
+#define FAKE_CXX     "__LIBIDE_FAKE_CXX__"
+#define FAKE_VALAC   "__LIBIDE_FAKE_VALAC__"
+#define PRINT_VARS   "include Makefile\nprint-%: ; @echo $* = $($*)\n"
 
 struct _IdeMakecache
 {
-  IdeObject    parent_instance;
+  IdeObject     parent_instance;
 
   GFile        *makefile;
   GFile        *parent;
@@ -52,6 +54,7 @@ struct _IdeMakecache
   GMappedFile  *mapped;
   EggTaskCache *file_targets_cache;
   EggTaskCache *file_flags_cache;
+  GPtrArray    *build_targets;
 };
 
 typedef struct
@@ -1391,6 +1394,7 @@ ide_makecache_finalize (GObject *object)
   g_clear_object (&self->file_targets_cache);
   g_clear_object (&self->file_flags_cache);
   g_clear_pointer (&self->llvm_flags, g_free);
+  g_clear_pointer (&self->build_targets, g_ptr_array_unref);
 
   G_OBJECT_CLASS (ide_makecache_parent_class)->finalize (object);
 
@@ -1707,3 +1711,382 @@ ide_makecache_get_file_flags_finish (IdeMakecache  *self,
 
   IDE_RETURN (ret);
 }
+
+static gboolean
+_find_make_directories (IdeMakecache  *self,
+                        GFile         *dir,
+                        GPtrArray     *ret,
+                        GCancellable  *cancellable,
+                        GError       **error)
+{
+  g_autoptr(GFileEnumerator) enumerator = NULL;
+  g_autoptr(GPtrArray) dirs = NULL;
+  gboolean has_makefile = FALSE;
+  gboolean has_makefile_am = FALSE;
+  GError *local_error = NULL;
+  gpointer infoptr;
+  guint i;
+
+  g_assert (IDE_IS_MAKECACHE (self));
+  g_assert (G_IS_FILE (dir));
+  g_assert (ret != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  enumerator = g_file_enumerate_children (dir,
+                                          G_FILE_ATTRIBUTE_STANDARD_NAME","
+                                          G_FILE_ATTRIBUTE_STANDARD_TYPE,
+                                          G_FILE_QUERY_INFO_NONE,
+                                          cancellable,
+                                          error);
+
+  dirs = g_ptr_array_new_with_free_func (g_object_unref);
+
+  while (NULL != (infoptr = g_file_enumerator_next_file (enumerator, cancellable, &local_error)))
+    {
+      g_autoptr(GFileInfo) info = infoptr;
+      const gchar *name;
+      GFileType type;
+
+      name = g_file_info_get_name (info);
+      type = g_file_info_get_file_type (info);
+
+      if (g_strcmp0 (name, "Makefile") == 0)
+        has_makefile = TRUE;
+      if (g_strcmp0 (name, "Makefile.am") == 0)
+        has_makefile_am = TRUE;
+      else if (type == G_FILE_TYPE_DIRECTORY)
+        g_ptr_array_add (dirs, g_file_get_child (dir, name));
+    }
+
+  if (local_error != NULL)
+    {
+      g_propagate_error (error, local_error);
+      return FALSE;
+    }
+
+  if (has_makefile && has_makefile_am)
+    g_ptr_array_add (ret, g_object_ref (dir));
+
+  if (!g_file_enumerator_close (enumerator, cancellable, error))
+    return FALSE;
+
+  for (i = 0; i < dirs->len; i++)
+    {
+      GFile *item = g_ptr_array_index (dirs, i);
+
+      if (!_find_make_directories (self, item, ret, cancellable, error))
+        return FALSE;
+    }
+
+  return TRUE;
+}
+
+static GPtrArray *
+find_make_directories (IdeMakecache  *self,
+                       GFile         *root,
+                       GCancellable  *cancellable,
+                       GError       **error)
+{
+  g_autoptr(GPtrArray) ret = NULL;
+
+  g_assert (IDE_IS_MAKECACHE (self));
+  g_assert (G_IS_FILE (root));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  /*
+   * TODO: Make this work for builddir != srcdir.
+   */
+
+  ret = g_ptr_array_new_with_free_func (g_object_unref);
+
+  if (!_find_make_directories (self, root, ret, cancellable, error))
+    return NULL;
+
+  if (ret->len == 0)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_NOT_FOUND,
+                   "No targets were found");
+      return NULL;
+    }
+
+  return g_steal_pointer (&ret);
+}
+
+static GFile *
+find_install_dir (const gchar *key,
+                  GHashTable  *dirs)
+{
+  g_auto(GStrv) parts = g_strsplit (key, "_", 2);
+  g_autofree gchar *lookup = g_strdup_printf ("%sdir", parts[0]);
+  const gchar *path = g_hash_table_lookup (dirs, lookup);
+
+  if (path != NULL)
+    return g_file_new_for_path (path);
+
+  return NULL;
+}
+
+static void
+ide_makecache_get_build_targets_worker (GTask        *task,
+                                        gpointer      source_object,
+                                        gpointer      task_data,
+                                        GCancellable *cancellable)
+{
+  IdeMakecache *self = source_object;
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autoptr(GPtrArray) makedirs = NULL;
+  g_autoptr(GPtrArray) targets = NULL;
+  g_autofree gchar *stdout_buf = NULL;
+  IdeConfigurationManager *configmgr;
+  IdeConfiguration *config;
+  const gchar *make_name = "make";
+  IdeContext *context;
+  IdeRuntime *runtime;
+  IdeVcs *vcs;
+  GFile *workdir;
+  GError *error = NULL;
+  gchar *line;
+  gsize line_len;
+  IdeLineReader reader;
+
+  IDE_ENTRY;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_MAKECACHE (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  /*
+   * This works by performing a dry run using the fake install path.  We then
+   * extract things that are installed into locations that look like they could
+   * be binaries. It's not foolproof, but generally gets the job done.
+   *
+   * We don't pass the tasks #GCancellable into many operations because we
+   * don't want the operation to fail. This is because we cache the results of
+   * this function and want them to be available for future access.
+   */
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  configmgr = ide_context_get_configuration_manager (context);
+  config = ide_configuration_manager_get_current (configmgr);
+  runtime = ide_configuration_get_runtime (config);
+  vcs = ide_context_get_vcs (context);
+  workdir = ide_vcs_get_working_directory (vcs);
+
+  if (runtime != NULL)
+    launcher = ide_runtime_create_launcher (runtime, NULL);
+
+  if (launcher == NULL)
+    {
+      g_autofree gchar *path = NULL;
+      path = g_file_get_path (workdir);
+
+      launcher = ide_subprocess_launcher_new (0);
+      ide_subprocess_launcher_set_cwd (launcher, path);
+    }
+
+  ide_subprocess_launcher_set_flags (launcher,
+                                     (G_SUBPROCESS_FLAGS_STDIN_PIPE |
+                                      G_SUBPROCESS_FLAGS_STDOUT_PIPE));
+
+  /* Default to "make" in runtimes other than the host, since we cannot
+   * rely on our configure-time check for the path there. This isn't totally
+   * correct, since we could be in jhbuild.
+   *
+   * TODO: We might want to rely on the runtime to discover basic utilities
+   *       like GNU Make.
+   */
+  if (g_strcmp0 (ide_configuration_get_runtime_id (config), "host") == 0)
+    make_name = GNU_MAKE_NAME;
+
+  ide_subprocess_launcher_push_argv (launcher, make_name);
+  ide_subprocess_launcher_push_argv (launcher, "-f");
+  ide_subprocess_launcher_push_argv (launcher, "-");
+  ide_subprocess_launcher_push_argv (launcher, "print-bindir");
+  ide_subprocess_launcher_push_argv (launcher, "print-libexecdir");
+  ide_subprocess_launcher_push_argv (launcher, "print-bin_PROGRAMS");
+  ide_subprocess_launcher_push_argv (launcher, "print-noinst_PROGRAMS");
+  ide_subprocess_launcher_push_argv (launcher, "print-libexec_PROGRAMS");
+
+  /*
+   * We need to extract the common automake targets from each of the
+   * directories that we know there is a standalone Makefile within.
+   */
+
+  makedirs = find_make_directories (self, workdir, cancellable, &error);
+
+  if (makedirs == NULL)
+    {
+      g_task_return_error (task, error);
+      IDE_GOTO (failure);
+    }
+
+  /*
+   * We need to extract various programs/libraries/targets from each of
+   * our make directories containing a Makefile.am (translated into a Makefile
+   * so that we can know what targets are available. With that knowledge, we
+   * can build our targets list and cache it for later.
+   */
+
+  targets = g_ptr_array_new_with_free_func (g_object_unref);
+
+  for (guint j = 0; j < makedirs->len; j++)
+    {
+      g_autoptr(GSubprocess) subprocess = NULL;
+      g_autoptr(GHashTable) amdirs = NULL;
+      g_autofree gchar *path = NULL;
+      GFile *makedir;
+
+      /*
+       * Make sure we are running within the directory containing the
+       * Makefile.am that we care about.
+       */
+      makedir = g_ptr_array_index (makedirs, j);
+      path = g_file_get_path (makedir);
+      ide_subprocess_launcher_set_cwd (launcher, path);
+
+      /*
+       * Spawn make, waiting for our stdin input which will add our debug
+       * printf target.
+       */
+      if (NULL == (subprocess = ide_subprocess_launcher_spawn_sync (launcher, NULL, &error)))
+        {
+          g_task_return_error (task, error);
+          IDE_GOTO (failure);
+        }
+
+      /*
+       * Write our helper target that will include the Makefile and then print
+       * debug variables we care about.
+       */
+      if (!g_subprocess_communicate_utf8 (subprocess, PRINT_VARS, NULL, &stdout_buf, NULL, &error))
+        {
+          g_task_return_error (task, error);
+          IDE_GOTO (failure);
+        }
+
+      amdirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+      /*
+       * Read through the output from make, and parse the installation targets
+       * that we care about.
+       */
+      ide_line_reader_init (&reader, stdout_buf, -1);
+
+      while (NULL != (line = ide_line_reader_next (&reader, &line_len)))
+        {
+          g_auto(GStrv) parts = NULL;
+          g_auto(GStrv) names = NULL;
+          const gchar *key;
+
+          line [line_len] = '\0';
+
+          parts = g_strsplit (line, "=", 2);
+
+          if (!parts[0] || !parts[1])
+            continue;
+
+          g_strstrip (parts [0]);
+          g_strstrip (parts [1]);
+
+          key = parts [0];
+
+          if (g_str_has_suffix (key, "dir"))
+            {
+              g_hash_table_insert (amdirs, g_strdup (key), g_strdup (parts [1]));
+              continue;
+            }
+
+          names = g_strsplit (parts [1], " ", 0);
+
+          for (guint i = 0; names [i]; i++)
+            {
+              g_autoptr(IdeBuildTarget) target = NULL;
+              g_autoptr(GFile) installdir = NULL;
+              const gchar *name = names [i];
+
+              installdir = find_install_dir (key, amdirs);
+
+              target = g_object_new (IDE_TYPE_AUTOTOOLS_BUILD_TARGET,
+                                     "build-directory", makedir,
+                                     "context", context,
+                                     "install-directory", installdir,
+                                     "name", name,
+                                     NULL);
+
+              g_ptr_array_add (targets, g_steal_pointer (&target));
+            }
+        }
+    }
+
+  g_task_return_pointer (task, g_steal_pointer (&targets), (GDestroyNotify)g_ptr_array_unref);
+
+failure:
+  IDE_EXIT;
+}
+
+void
+ide_makecache_get_build_targets_async (IdeMakecache        *self,
+                                       GCancellable        *cancellable,
+                                       GAsyncReadyCallback  callback,
+                                       gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  GPtrArray *ret;
+  guint i;
+
+  g_return_if_fail (IDE_IS_MAKECACHE (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_makecache_get_build_targets_async);
+  g_task_set_check_cancellable (task, FALSE);
+
+  if (self->build_targets == NULL)
+    {
+      g_task_run_in_thread (task, ide_makecache_get_build_targets_worker);
+      return;
+    }
+
+  ret = g_ptr_array_new_with_free_func (g_object_unref);
+
+  for (i = 0; i < self->build_targets->len; i++)
+    {
+      IdeBuildTarget *target = g_ptr_array_index (self->build_targets, i);
+
+      g_ptr_array_add (ret, g_object_ref (target));
+    }
+
+  g_task_return_pointer (task, ret, (GDestroyNotify)g_ptr_array_unref);
+}
+
+GPtrArray *
+ide_makecache_get_build_targets_finish (IdeMakecache  *self,
+                                        GAsyncResult  *result,
+                                        GError       **error)
+{
+  GPtrArray *ret;
+
+  g_return_val_if_fail (IDE_IS_MAKECACHE (self), NULL);
+  g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+  ret = g_task_propagate_pointer (G_TASK (result), error);
+
+  /*
+   * Save a copy of all the build targets for future lookups.
+   */
+  if (ret != NULL && self->build_targets == NULL)
+    {
+      self->build_targets = g_ptr_array_new_with_free_func (g_object_unref);
+
+      for (guint i = 0; i < ret->len; i++)
+        {
+          IdeBuildTarget *item = g_ptr_array_index (ret, i);
+
+          g_ptr_array_add (self->build_targets, g_object_ref (item));
+        }
+    }
+
+  return ret;
+}
diff --git a/plugins/autotools/ide-makecache.h b/plugins/autotools/ide-makecache.h
index 42ed3d0..9fba3c2 100644
--- a/plugins/autotools/ide-makecache.h
+++ b/plugins/autotools/ide-makecache.h
@@ -29,30 +29,37 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeMakecache, ide_makecache, IDE, MAKECACHE, IdeObject)
 
-void                 ide_makecache_new_for_makefile_async  (IdeContext           *context,
-                                                            GFile                *makefile,
-                                                            GCancellable         *cancellable,
-                                                            GAsyncReadyCallback   callback,
-                                                            gpointer              user_data);
-IdeMakecache        *ide_makecache_new_for_makefile_finish (GAsyncResult         *result,
-                                                            GError              **error);
-GFile               *ide_makecache_get_makefile            (IdeMakecache         *self);
-void                 ide_makecache_get_file_flags_async    (IdeMakecache         *self,
-                                                            GFile                *file,
-                                                            GCancellable         *cancellable,
-                                                            GAsyncReadyCallback   callback,
-                                                            gpointer              user_data);
-gchar              **ide_makecache_get_file_flags_finish   (IdeMakecache         *self,
-                                                            GAsyncResult         *result,
-                                                            GError              **error);
-void                 ide_makecache_get_file_targets_async  (IdeMakecache         *self,
-                                                            GFile                *file,
-                                                            GCancellable         *cancellable,
-                                                            GAsyncReadyCallback   callback,
-                                                            gpointer              user_data);
-GPtrArray           *ide_makecache_get_file_targets_finish (IdeMakecache         *self,
-                                                            GAsyncResult         *result,
-                                                            GError              **error);
+void                 ide_makecache_new_for_makefile_async   (IdeContext           *context,
+                                                             GFile                *makefile,
+                                                             GCancellable         *cancellable,
+                                                             GAsyncReadyCallback   callback,
+                                                             gpointer              user_data);
+IdeMakecache        *ide_makecache_new_for_makefile_finish  (GAsyncResult         *result,
+                                                             GError              **error);
+GFile               *ide_makecache_get_makefile             (IdeMakecache         *self);
+void                 ide_makecache_get_file_flags_async     (IdeMakecache         *self,
+                                                             GFile                *file,
+                                                             GCancellable         *cancellable,
+                                                             GAsyncReadyCallback   callback,
+                                                             gpointer              user_data);
+gchar              **ide_makecache_get_file_flags_finish    (IdeMakecache         *self,
+                                                             GAsyncResult         *result,
+                                                             GError              **error);
+void                 ide_makecache_get_file_targets_async   (IdeMakecache         *self,
+                                                             GFile                *file,
+                                                             GCancellable         *cancellable,
+                                                             GAsyncReadyCallback   callback,
+                                                             gpointer              user_data);
+GPtrArray           *ide_makecache_get_file_targets_finish  (IdeMakecache         *self,
+                                                             GAsyncResult         *result,
+                                                             GError              **error);
+void                 ide_makecache_get_build_targets_async  (IdeMakecache         *self,
+                                                             GCancellable         *cancellable,
+                                                             GAsyncReadyCallback   callback,
+                                                             gpointer              user_data);
+GPtrArray           *ide_makecache_get_build_targets_finish (IdeMakecache         *self,
+                                                             GAsyncResult         *result,
+                                                             GError              **error);
 
 G_END_DECLS
 



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