Commit 3996923e authored by Christian Hergert's avatar Christian Hergert Committed by Matthew Leeds
Browse files

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.
parent 334c5840
......@@ -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 \
......
......@@ -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);
}
......@@ -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",
......
/* 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)
{
}
/* 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 */
......@@ -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)
{
......@@ -322,6 +298,333 @@ ide_autotools_build_system_get_priority (IdeBuildSystem *system)
return -100;
}
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;