mirror of
https://github.com/flatpak/flatpak.git
synced 2026-01-20 13:48:26 -05:00
Instead of mixing the source bundling with the build we make it a separate step at the end, with cache support just like the other stages. Being at the end means we can reuse the cached stages from the build if we enable bundle-sources after an existing build. Also, this changes how the json and local files/patches are stored. Now they are in a subdirectory called "manifest" in the sources directory, with proper handling of relative pathnames for included modules, etc. This also means we don't look for these file in the extra-sources directory, but rather next to the json like we do normally.
1815 lines
58 KiB
C
1815 lines
58 KiB
C
/* builder-module.c
|
|
*
|
|
* Copyright (C) 2015 Red Hat, Inc
|
|
*
|
|
* This file is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This file 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Authors:
|
|
* Alexander Larsson <alexl@redhat.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/statfs.h>
|
|
|
|
#include <gio/gio.h>
|
|
#include "libglnx/libglnx.h"
|
|
|
|
#include "flatpak-utils.h"
|
|
#include "builder-utils.h"
|
|
#include "builder-module.h"
|
|
#include "builder-post-process.h"
|
|
#include "builder-manifest.h"
|
|
|
|
struct BuilderModule
|
|
{
|
|
GObject parent;
|
|
|
|
char *json_path;
|
|
char *name;
|
|
char *subdir;
|
|
char **post_install;
|
|
char **config_opts;
|
|
char **make_args;
|
|
char **make_install_args;
|
|
char *buildsystem;
|
|
char **ensure_writable;
|
|
char **only_arches;
|
|
char **skip_arches;
|
|
gboolean disabled;
|
|
gboolean rm_configure;
|
|
gboolean no_autogen;
|
|
gboolean no_parallel_make;
|
|
gboolean no_make_install;
|
|
gboolean no_python_timestamp_fix;
|
|
gboolean cmake;
|
|
gboolean builddir;
|
|
BuilderOptions *build_options;
|
|
GPtrArray *changes;
|
|
char **cleanup;
|
|
char **cleanup_platform;
|
|
GList *sources;
|
|
GList *modules;
|
|
char **build_commands;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
GObjectClass parent_class;
|
|
} BuilderModuleClass;
|
|
|
|
static void serializable_iface_init (JsonSerializableIface *serializable_iface);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (BuilderModule, builder_module, G_TYPE_OBJECT,
|
|
G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE, serializable_iface_init));
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_NAME,
|
|
PROP_SUBDIR,
|
|
PROP_RM_CONFIGURE,
|
|
PROP_DISABLED,
|
|
PROP_NO_AUTOGEN,
|
|
PROP_NO_PARALLEL_MAKE,
|
|
PROP_NO_MAKE_INSTALL,
|
|
PROP_NO_PYTHON_TIMESTAMP_FIX,
|
|
PROP_CMAKE,
|
|
PROP_BUILDSYSTEM,
|
|
PROP_BUILDDIR,
|
|
PROP_CONFIG_OPTS,
|
|
PROP_MAKE_ARGS,
|
|
PROP_MAKE_INSTALL_ARGS,
|
|
PROP_ENSURE_WRITABLE,
|
|
PROP_ONLY_ARCHES,
|
|
PROP_SKIP_ARCHES,
|
|
PROP_SOURCES,
|
|
PROP_BUILD_OPTIONS,
|
|
PROP_CLEANUP,
|
|
PROP_CLEANUP_PLATFORM,
|
|
PROP_POST_INSTALL,
|
|
PROP_MODULES,
|
|
PROP_BUILD_COMMANDS,
|
|
LAST_PROP
|
|
};
|
|
|
|
static void
|
|
collect_cleanup_for_path (const char **patterns,
|
|
const char *path,
|
|
const char *add_prefix,
|
|
GHashTable *to_remove_ht)
|
|
{
|
|
int i;
|
|
|
|
if (patterns == NULL)
|
|
return;
|
|
|
|
for (i = 0; patterns[i] != NULL; i++)
|
|
flatpak_collect_matches_for_path_pattern (path, patterns[i], add_prefix, to_remove_ht);
|
|
}
|
|
|
|
static void
|
|
builder_module_finalize (GObject *object)
|
|
{
|
|
BuilderModule *self = (BuilderModule *) object;
|
|
|
|
g_free (self->json_path);
|
|
g_free (self->name);
|
|
g_free (self->subdir);
|
|
g_free (self->buildsystem);
|
|
g_strfreev (self->post_install);
|
|
g_strfreev (self->config_opts);
|
|
g_strfreev (self->make_args);
|
|
g_strfreev (self->make_install_args);
|
|
g_strfreev (self->ensure_writable);
|
|
g_strfreev (self->only_arches);
|
|
g_strfreev (self->skip_arches);
|
|
g_clear_object (&self->build_options);
|
|
g_list_free_full (self->sources, g_object_unref);
|
|
g_strfreev (self->cleanup);
|
|
g_strfreev (self->cleanup_platform);
|
|
g_list_free_full (self->modules, g_object_unref);
|
|
g_strfreev (self->build_commands);
|
|
|
|
if (self->changes)
|
|
g_ptr_array_unref (self->changes);
|
|
|
|
G_OBJECT_CLASS (builder_module_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
builder_module_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
BuilderModule *self = BUILDER_MODULE (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_NAME:
|
|
g_value_set_string (value, self->name);
|
|
break;
|
|
|
|
case PROP_SUBDIR:
|
|
g_value_set_string (value, self->subdir);
|
|
break;
|
|
|
|
case PROP_RM_CONFIGURE:
|
|
g_value_set_boolean (value, self->rm_configure);
|
|
break;
|
|
|
|
case PROP_DISABLED:
|
|
g_value_set_boolean (value, self->disabled);
|
|
break;
|
|
|
|
case PROP_NO_AUTOGEN:
|
|
g_value_set_boolean (value, self->no_autogen);
|
|
break;
|
|
|
|
case PROP_NO_PARALLEL_MAKE:
|
|
g_value_set_boolean (value, self->no_parallel_make);
|
|
break;
|
|
|
|
case PROP_NO_MAKE_INSTALL:
|
|
g_value_set_boolean (value, self->no_make_install);
|
|
break;
|
|
|
|
case PROP_NO_PYTHON_TIMESTAMP_FIX:
|
|
g_value_set_boolean (value, self->no_python_timestamp_fix);
|
|
break;
|
|
|
|
case PROP_CMAKE:
|
|
g_value_set_boolean (value, self->cmake);
|
|
break;
|
|
|
|
case PROP_BUILDSYSTEM:
|
|
g_value_set_string (value, self->buildsystem);
|
|
break;
|
|
|
|
case PROP_BUILDDIR:
|
|
g_value_set_boolean (value, self->builddir);
|
|
break;
|
|
|
|
case PROP_CONFIG_OPTS:
|
|
g_value_set_boxed (value, self->config_opts);
|
|
break;
|
|
|
|
case PROP_MAKE_ARGS:
|
|
g_value_set_boxed (value, self->make_args);
|
|
break;
|
|
|
|
case PROP_MAKE_INSTALL_ARGS:
|
|
g_value_set_boxed (value, self->make_install_args);
|
|
break;
|
|
|
|
case PROP_ENSURE_WRITABLE:
|
|
g_value_set_boxed (value, self->ensure_writable);
|
|
break;
|
|
|
|
case PROP_ONLY_ARCHES:
|
|
g_value_set_boxed (value, self->only_arches);
|
|
break;
|
|
|
|
case PROP_SKIP_ARCHES:
|
|
g_value_set_boxed (value, self->skip_arches);
|
|
break;
|
|
|
|
case PROP_POST_INSTALL:
|
|
g_value_set_boxed (value, self->post_install);
|
|
break;
|
|
|
|
case PROP_BUILD_OPTIONS:
|
|
g_value_set_object (value, self->build_options);
|
|
break;
|
|
|
|
case PROP_SOURCES:
|
|
g_value_set_pointer (value, self->sources);
|
|
break;
|
|
|
|
case PROP_CLEANUP:
|
|
g_value_set_boxed (value, self->cleanup);
|
|
break;
|
|
|
|
case PROP_CLEANUP_PLATFORM:
|
|
g_value_set_boxed (value, self->cleanup_platform);
|
|
break;
|
|
|
|
case PROP_MODULES:
|
|
g_value_set_pointer (value, self->modules);
|
|
break;
|
|
|
|
case PROP_BUILD_COMMANDS:
|
|
g_value_set_boxed (value, self->build_commands);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
builder_module_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
BuilderModule *self = BUILDER_MODULE (object);
|
|
gchar **tmp;
|
|
char *p;
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_NAME:
|
|
g_clear_pointer (&self->name, g_free);
|
|
self->name = g_value_dup_string (value);
|
|
if ((p = strchr (self->name, ' ')) ||
|
|
(p = strchr (self->name, '/')))
|
|
g_printerr ("Module names like '%s' containing '%c' are problematic. Expect errors.\n", self->name, *p);
|
|
break;
|
|
|
|
case PROP_SUBDIR:
|
|
g_clear_pointer (&self->subdir, g_free);
|
|
self->subdir = g_value_dup_string (value);
|
|
break;
|
|
|
|
case PROP_RM_CONFIGURE:
|
|
self->rm_configure = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_DISABLED:
|
|
self->disabled = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_NO_AUTOGEN:
|
|
self->no_autogen = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_NO_PARALLEL_MAKE:
|
|
self->no_parallel_make = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_NO_MAKE_INSTALL:
|
|
self->no_make_install = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_NO_PYTHON_TIMESTAMP_FIX:
|
|
self->no_python_timestamp_fix = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_CMAKE:
|
|
self->cmake = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_BUILDSYSTEM:
|
|
g_free (self->buildsystem);
|
|
self->buildsystem = g_value_dup_string (value);
|
|
break;
|
|
|
|
case PROP_BUILDDIR:
|
|
self->builddir = g_value_get_boolean (value);
|
|
break;
|
|
|
|
case PROP_CONFIG_OPTS:
|
|
tmp = self->config_opts;
|
|
self->config_opts = g_strdupv (g_value_get_boxed (value));
|
|
g_strfreev (tmp);
|
|
break;
|
|
|
|
case PROP_MAKE_ARGS:
|
|
tmp = self->make_args;
|
|
self->make_args = g_strdupv (g_value_get_boxed (value));
|
|
g_strfreev (tmp);
|
|
break;
|
|
|
|
case PROP_MAKE_INSTALL_ARGS:
|
|
tmp = self->make_install_args;
|
|
self->make_install_args = g_strdupv (g_value_get_boxed (value));
|
|
g_strfreev (tmp);
|
|
break;
|
|
|
|
case PROP_ENSURE_WRITABLE:
|
|
tmp = self->ensure_writable;
|
|
self->ensure_writable = g_strdupv (g_value_get_boxed (value));
|
|
g_strfreev (tmp);
|
|
break;
|
|
|
|
case PROP_ONLY_ARCHES:
|
|
tmp = self->only_arches;
|
|
self->only_arches = g_strdupv (g_value_get_boxed (value));
|
|
g_strfreev (tmp);
|
|
break;
|
|
|
|
case PROP_SKIP_ARCHES:
|
|
tmp = self->skip_arches;
|
|
self->skip_arches = g_strdupv (g_value_get_boxed (value));
|
|
g_strfreev (tmp);
|
|
break;
|
|
|
|
case PROP_POST_INSTALL:
|
|
tmp = self->post_install;
|
|
self->post_install = g_strdupv (g_value_get_boxed (value));
|
|
g_strfreev (tmp);
|
|
break;
|
|
|
|
case PROP_BUILD_OPTIONS:
|
|
g_set_object (&self->build_options, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_SOURCES:
|
|
g_list_free_full (self->sources, g_object_unref);
|
|
/* NOTE: This takes ownership of the list! */
|
|
self->sources = g_value_get_pointer (value);
|
|
break;
|
|
|
|
case PROP_CLEANUP:
|
|
tmp = self->cleanup;
|
|
self->cleanup = g_strdupv (g_value_get_boxed (value));
|
|
g_strfreev (tmp);
|
|
break;
|
|
|
|
case PROP_CLEANUP_PLATFORM:
|
|
tmp = self->cleanup_platform;
|
|
self->cleanup_platform = g_strdupv (g_value_get_boxed (value));
|
|
g_strfreev (tmp);
|
|
break;
|
|
|
|
case PROP_MODULES:
|
|
g_list_free_full (self->modules, g_object_unref);
|
|
/* NOTE: This takes ownership of the list! */
|
|
self->modules = g_value_get_pointer (value);
|
|
break;
|
|
|
|
case PROP_BUILD_COMMANDS:
|
|
tmp = self->build_commands;
|
|
self->build_commands = g_strdupv (g_value_get_boxed (value));
|
|
g_strfreev (tmp);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
builder_module_class_init (BuilderModuleClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
object_class->finalize = builder_module_finalize;
|
|
object_class->get_property = builder_module_get_property;
|
|
object_class->set_property = builder_module_set_property;
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_NAME,
|
|
g_param_spec_string ("name",
|
|
"",
|
|
"",
|
|
NULL,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_SUBDIR,
|
|
g_param_spec_string ("subdir",
|
|
"",
|
|
"",
|
|
NULL,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_RM_CONFIGURE,
|
|
g_param_spec_boolean ("rm-configure",
|
|
"",
|
|
"",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_DISABLED,
|
|
g_param_spec_boolean ("disabled",
|
|
"",
|
|
"",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_NO_AUTOGEN,
|
|
g_param_spec_boolean ("no-autogen",
|
|
"",
|
|
"",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_NO_PARALLEL_MAKE,
|
|
g_param_spec_boolean ("no-parallel-make",
|
|
"",
|
|
"",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_NO_MAKE_INSTALL,
|
|
g_param_spec_boolean ("no-make-install",
|
|
"",
|
|
"",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_NO_PYTHON_TIMESTAMP_FIX,
|
|
g_param_spec_boolean ("no-python-timestamp-fix",
|
|
"",
|
|
"",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_CMAKE,
|
|
g_param_spec_boolean ("cmake",
|
|
"",
|
|
"",
|
|
FALSE,
|
|
G_PARAM_READWRITE|G_PARAM_DEPRECATED));
|
|
g_object_class_install_property (object_class,
|
|
PROP_BUILDSYSTEM,
|
|
g_param_spec_string ("buildsystem",
|
|
"",
|
|
"",
|
|
NULL,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_BUILDDIR,
|
|
g_param_spec_boolean ("builddir",
|
|
"",
|
|
"",
|
|
FALSE,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_SOURCES,
|
|
g_param_spec_pointer ("sources",
|
|
"",
|
|
"",
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_CONFIG_OPTS,
|
|
g_param_spec_boxed ("config-opts",
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_MAKE_ARGS,
|
|
g_param_spec_boxed ("make-args",
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_MAKE_INSTALL_ARGS,
|
|
g_param_spec_boxed ("make-install-args",
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_ENSURE_WRITABLE,
|
|
g_param_spec_boxed ("ensure-writable",
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_ONLY_ARCHES,
|
|
g_param_spec_boxed ("only-arches",
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_SKIP_ARCHES,
|
|
g_param_spec_boxed ("skip-arches",
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_POST_INSTALL,
|
|
g_param_spec_boxed ("post-install",
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_BUILD_OPTIONS,
|
|
g_param_spec_object ("build-options",
|
|
"",
|
|
"",
|
|
BUILDER_TYPE_OPTIONS,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_CLEANUP,
|
|
g_param_spec_boxed ("cleanup",
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_CLEANUP_PLATFORM,
|
|
g_param_spec_boxed ("cleanup-platform",
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_MODULES,
|
|
g_param_spec_pointer ("modules",
|
|
"",
|
|
"",
|
|
G_PARAM_READWRITE));
|
|
g_object_class_install_property (object_class,
|
|
PROP_BUILD_COMMANDS,
|
|
g_param_spec_boxed ("build-commands",
|
|
"",
|
|
"",
|
|
G_TYPE_STRV,
|
|
G_PARAM_READWRITE));
|
|
}
|
|
|
|
static void
|
|
builder_module_init (BuilderModule *self)
|
|
{
|
|
}
|
|
|
|
static JsonNode *
|
|
builder_module_serialize_property (JsonSerializable *serializable,
|
|
const gchar *property_name,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
if (strcmp (property_name, "modules") == 0)
|
|
{
|
|
BuilderModule *self = BUILDER_MODULE (serializable);
|
|
JsonNode *retval = NULL;
|
|
GList *l;
|
|
|
|
if (self->modules)
|
|
{
|
|
JsonArray *array;
|
|
|
|
array = json_array_sized_new (g_list_length (self->modules));
|
|
|
|
for (l = self->modules; l != NULL; l = l->next)
|
|
{
|
|
JsonNode *child = json_gobject_serialize (l->data);
|
|
json_array_add_element (array, child);
|
|
}
|
|
|
|
retval = json_node_init_array (json_node_alloc (), array);
|
|
json_array_unref (array);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
else if (strcmp (property_name, "sources") == 0)
|
|
{
|
|
BuilderModule *self = BUILDER_MODULE (serializable);
|
|
JsonNode *retval = NULL;
|
|
GList *l;
|
|
|
|
if (self->sources)
|
|
{
|
|
JsonArray *array;
|
|
|
|
array = json_array_sized_new (g_list_length (self->sources));
|
|
|
|
for (l = self->sources; l != NULL; l = l->next)
|
|
{
|
|
JsonNode *child = builder_source_to_json (BUILDER_SOURCE (l->data));
|
|
json_array_add_element (array, child);
|
|
}
|
|
|
|
retval = json_node_init_array (json_node_alloc (), array);
|
|
json_array_unref (array);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
else
|
|
{
|
|
return json_serializable_default_serialize_property (serializable,
|
|
property_name,
|
|
value,
|
|
pspec);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
builder_module_deserialize_property (JsonSerializable *serializable,
|
|
const gchar *property_name,
|
|
GValue *value,
|
|
GParamSpec *pspec,
|
|
JsonNode *property_node)
|
|
{
|
|
if (strcmp (property_name, "modules") == 0)
|
|
{
|
|
if (JSON_NODE_TYPE (property_node) == JSON_NODE_NULL)
|
|
{
|
|
g_value_set_pointer (value, NULL);
|
|
return TRUE;
|
|
}
|
|
else if (JSON_NODE_TYPE (property_node) == JSON_NODE_ARRAY)
|
|
{
|
|
JsonArray *array = json_node_get_array (property_node);
|
|
guint i, array_len = json_array_get_length (array);
|
|
g_autoptr(GFile) saved_demarshal_base_dir = builder_manifest_get_demarshal_base_dir ();
|
|
GList *modules = NULL;
|
|
GObject *module;
|
|
|
|
for (i = 0; i < array_len; i++)
|
|
{
|
|
JsonNode *element_node = json_array_get_element (array, i);
|
|
|
|
module = NULL;
|
|
|
|
if (JSON_NODE_HOLDS_VALUE (element_node) &&
|
|
json_node_get_value_type (element_node) == G_TYPE_STRING)
|
|
{
|
|
const char *module_relpath = json_node_get_string (element_node);
|
|
g_autoptr(GFile) module_file =
|
|
g_file_resolve_relative_path (saved_demarshal_base_dir, module_relpath);
|
|
const char *module_path = flatpak_file_get_path_cached (module_file);
|
|
g_autofree char *json = NULL;
|
|
|
|
if (g_file_get_contents (module_path, &json, NULL, NULL))
|
|
{
|
|
g_autoptr(GFile) module_file_dir = g_file_get_parent (module_file);
|
|
builder_manifest_set_demarshal_base_dir (module_file_dir);
|
|
module = json_gobject_from_data (BUILDER_TYPE_MODULE,
|
|
json, -1, NULL);
|
|
builder_manifest_set_demarshal_base_dir (saved_demarshal_base_dir);
|
|
if (module)
|
|
{
|
|
builder_module_set_json_path (BUILDER_MODULE (module), module_path);
|
|
builder_module_set_base_dir (BUILDER_MODULE (module), module_file_dir);
|
|
}
|
|
}
|
|
}
|
|
else if (JSON_NODE_HOLDS_OBJECT (element_node))
|
|
{
|
|
module = json_gobject_deserialize (BUILDER_TYPE_MODULE, element_node);
|
|
if (module != NULL)
|
|
builder_module_set_base_dir (BUILDER_MODULE (module), saved_demarshal_base_dir);
|
|
}
|
|
|
|
if (module == NULL)
|
|
{
|
|
g_list_free_full (modules, g_object_unref);
|
|
return FALSE;
|
|
}
|
|
|
|
modules = g_list_prepend (modules, module);
|
|
}
|
|
|
|
g_value_set_pointer (value, g_list_reverse (modules));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
else if (strcmp (property_name, "sources") == 0)
|
|
{
|
|
if (JSON_NODE_TYPE (property_node) == JSON_NODE_NULL)
|
|
{
|
|
g_value_set_pointer (value, NULL);
|
|
return TRUE;
|
|
}
|
|
else if (JSON_NODE_TYPE (property_node) == JSON_NODE_ARRAY)
|
|
{
|
|
JsonArray *array = json_node_get_array (property_node);
|
|
guint i, array_len = json_array_get_length (array);
|
|
GList *sources = NULL;
|
|
BuilderSource *source;
|
|
|
|
for (i = 0; i < array_len; i++)
|
|
{
|
|
JsonNode *element_node = json_array_get_element (array, i);
|
|
|
|
if (JSON_NODE_TYPE (element_node) != JSON_NODE_OBJECT)
|
|
{
|
|
g_list_free_full (sources, g_object_unref);
|
|
return FALSE;
|
|
}
|
|
|
|
source = builder_source_from_json (element_node);
|
|
if (source == NULL)
|
|
{
|
|
g_list_free_full (sources, g_object_unref);
|
|
return FALSE;
|
|
}
|
|
|
|
sources = g_list_prepend (sources, source);
|
|
}
|
|
|
|
g_value_set_pointer (value, g_list_reverse (sources));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
return json_serializable_default_deserialize_property (serializable,
|
|
property_name,
|
|
value,
|
|
pspec, property_node);
|
|
}
|
|
}
|
|
|
|
static void
|
|
serializable_iface_init (JsonSerializableIface *serializable_iface)
|
|
{
|
|
serializable_iface->serialize_property = builder_module_serialize_property;
|
|
serializable_iface->deserialize_property = builder_module_deserialize_property;
|
|
serializable_iface->find_property = builder_serializable_find_property_with_error;
|
|
}
|
|
|
|
const char *
|
|
builder_module_get_name (BuilderModule *self)
|
|
{
|
|
return self->name;
|
|
}
|
|
|
|
gboolean
|
|
builder_module_is_enabled (BuilderModule *self,
|
|
BuilderContext *context)
|
|
{
|
|
if (self->disabled)
|
|
return FALSE;
|
|
|
|
if (self->only_arches != NULL &&
|
|
self->only_arches[0] != NULL &&
|
|
!g_strv_contains ((const char * const *) self->only_arches, builder_context_get_arch (context)))
|
|
return FALSE;
|
|
|
|
if (self->skip_arches != NULL &&
|
|
g_strv_contains ((const char * const *)self->skip_arches, builder_context_get_arch (context)))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
builder_module_get_disabled (BuilderModule *self)
|
|
{
|
|
return self->disabled;
|
|
}
|
|
|
|
GList *
|
|
builder_module_get_sources (BuilderModule *self)
|
|
{
|
|
return self->sources;
|
|
}
|
|
|
|
GList *
|
|
builder_module_get_modules (BuilderModule *self)
|
|
{
|
|
return self->modules;
|
|
}
|
|
|
|
gboolean
|
|
builder_module_show_deps (BuilderModule *self,
|
|
BuilderContext *context,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
if (self->json_path)
|
|
g_print ("%s\n", self->json_path);
|
|
|
|
for (l = self->sources; l != NULL; l = l->next)
|
|
{
|
|
BuilderSource *source = l->data;
|
|
|
|
if (!builder_source_is_enabled (source, context))
|
|
continue;
|
|
|
|
if (!builder_source_show_deps (source, error))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
builder_module_download_sources (BuilderModule *self,
|
|
gboolean update_vcs,
|
|
BuilderContext *context,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = self->sources; l != NULL; l = l->next)
|
|
{
|
|
BuilderSource *source = l->data;
|
|
|
|
if (!builder_source_is_enabled (source, context))
|
|
continue;
|
|
|
|
if (!builder_source_download (source, update_vcs, context, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
builder_module_extract_sources (BuilderModule *self,
|
|
GFile *dest,
|
|
BuilderContext *context,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
|
|
if (!g_file_query_exists (dest, NULL) &&
|
|
!g_file_make_directory_with_parents (dest, NULL, error))
|
|
return FALSE;
|
|
|
|
for (l = self->sources; l != NULL; l = l->next)
|
|
{
|
|
BuilderSource *source = l->data;
|
|
|
|
if (!builder_source_is_enabled (source, context))
|
|
continue;
|
|
|
|
if (!builder_source_extract (source, dest, self->build_options, context, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
builder_module_bundle_sources (BuilderModule *self,
|
|
BuilderContext *context,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
|
|
if (self->json_path)
|
|
{
|
|
g_autoptr(GFile) json_file = g_file_new_for_path (self->json_path);
|
|
g_autoptr(GFile) destination_file = NULL;
|
|
g_autoptr(GFile) destination_dir = NULL;
|
|
GFile *manifest_base_dir = builder_context_get_base_dir (context);
|
|
g_autofree char *rel_path = g_file_get_relative_path (manifest_base_dir, json_file);
|
|
|
|
if (rel_path == NULL)
|
|
{
|
|
g_warning ("Included manifest %s is outside manifest tree, not bundling", self->json_path);
|
|
return TRUE;
|
|
}
|
|
|
|
destination_file = flatpak_build_file (builder_context_get_app_dir (context),
|
|
"sources/manifest", rel_path, NULL);
|
|
|
|
destination_dir = g_file_get_parent (destination_file);
|
|
if (!flatpak_mkdir_p (destination_dir, NULL, error))
|
|
return FALSE;
|
|
|
|
if (!g_file_copy (json_file, destination_file,
|
|
G_FILE_COPY_OVERWRITE,
|
|
NULL,
|
|
NULL, NULL,
|
|
error))
|
|
return FALSE;
|
|
}
|
|
|
|
for (l = self->sources; l != NULL; l = l->next)
|
|
{
|
|
BuilderSource *source = l->data;
|
|
|
|
if (!builder_source_is_enabled (source, context))
|
|
continue;
|
|
|
|
if (!builder_source_bundle (source, context, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GPtrArray *
|
|
setup_build_args (GFile *app_dir,
|
|
const char *module_name,
|
|
BuilderContext *context,
|
|
GFile *source_dir,
|
|
const char *cwd_subdir,
|
|
char **flatpak_opts,
|
|
char **env_vars,
|
|
GFile **cwd_file)
|
|
{
|
|
g_autoptr(GPtrArray) args = NULL;
|
|
g_autofree char *source_dir_path = g_file_get_path (source_dir);
|
|
g_autofree char *source_dir_path_canonical = NULL;
|
|
g_autofree char *ccache_dir_path = NULL;
|
|
const char *builddir;
|
|
int i;
|
|
|
|
args = g_ptr_array_new_with_free_func (g_free);
|
|
g_ptr_array_add (args, g_strdup ("flatpak"));
|
|
g_ptr_array_add (args, g_strdup ("build"));
|
|
|
|
source_dir_path_canonical = realpath (source_dir_path, NULL);
|
|
|
|
if (builder_context_get_build_runtime (context))
|
|
builddir = "/run/build-runtime/";
|
|
else
|
|
builddir = "/run/build/";
|
|
|
|
g_ptr_array_add (args, g_strdup ("--nofilesystem=host"));
|
|
|
|
/* We mount the canonical location, because bind-mounts of symlinks don't really work */
|
|
g_ptr_array_add (args, g_strdup_printf ("--filesystem=%s", source_dir_path_canonical));
|
|
|
|
/* Also make sure the original path is available (if it was not canonical, in case something references that. */
|
|
if (strcmp (source_dir_path_canonical, source_dir_path) != 0)
|
|
g_ptr_array_add (args, g_strdup_printf ("--bind-mount=%s=%s", source_dir_path, source_dir_path_canonical));
|
|
|
|
g_ptr_array_add (args, g_strdup_printf ("--bind-mount=%s%s=%s", builddir, module_name, source_dir_path_canonical));
|
|
if (cwd_subdir)
|
|
g_ptr_array_add (args, g_strdup_printf ("--build-dir=%s%s/%s", builddir, module_name, cwd_subdir));
|
|
else
|
|
g_ptr_array_add (args, g_strdup_printf ("--build-dir=%s%s", builddir, module_name));
|
|
|
|
if (g_file_query_exists (builder_context_get_ccache_dir (context), NULL))
|
|
{
|
|
ccache_dir_path = g_file_get_path (builder_context_get_ccache_dir (context));
|
|
g_ptr_array_add (args, g_strdup_printf ("--bind-mount=/run/ccache=%s", ccache_dir_path));
|
|
}
|
|
|
|
if (flatpak_opts)
|
|
{
|
|
for (i = 0; flatpak_opts[i] != NULL; i++)
|
|
g_ptr_array_add (args, g_strdup (flatpak_opts[i]));
|
|
}
|
|
|
|
if (env_vars)
|
|
{
|
|
for (i = 0; env_vars[i] != NULL; i++)
|
|
g_ptr_array_add (args, g_strdup_printf ("--env=%s", env_vars[i]));
|
|
}
|
|
|
|
g_ptr_array_add (args, g_file_get_path (app_dir));
|
|
|
|
*cwd_file = g_file_new_for_path (source_dir_path_canonical);
|
|
|
|
return g_steal_pointer (&args);
|
|
}
|
|
|
|
static gboolean
|
|
shell (GFile *app_dir,
|
|
const char *module_name,
|
|
BuilderContext *context,
|
|
GFile *source_dir,
|
|
const char *cwd_subdir,
|
|
char **flatpak_opts,
|
|
char **env_vars,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GFile) cwd_file = NULL;
|
|
g_autoptr(GPtrArray) args =
|
|
setup_build_args (app_dir, module_name, context, source_dir, cwd_subdir, flatpak_opts, env_vars, &cwd_file);
|
|
|
|
g_ptr_array_add (args, "sh");
|
|
g_ptr_array_add (args, NULL);
|
|
|
|
if (chdir (flatpak_file_get_path_cached (cwd_file)))
|
|
{
|
|
glnx_set_error_from_errno (error);
|
|
return FALSE;
|
|
}
|
|
|
|
if (execvp ((char *) args->pdata[0], (char **) args->pdata) == -1)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Unable to start flatpak build");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static const char skip_arg[] = "skip";
|
|
static const char strv_arg[] = "strv";
|
|
|
|
static gboolean
|
|
build (GFile *app_dir,
|
|
const char *module_name,
|
|
BuilderContext *context,
|
|
GFile *source_dir,
|
|
const char *cwd_subdir,
|
|
char **flatpak_opts,
|
|
char **env_vars,
|
|
GError **error,
|
|
const gchar *argv1,
|
|
...)
|
|
{
|
|
g_autoptr(GFile) cwd_file = NULL;
|
|
g_autoptr(GPtrArray) args =
|
|
setup_build_args (app_dir, module_name, context, source_dir, cwd_subdir, flatpak_opts, env_vars, &cwd_file);
|
|
const gchar *arg;
|
|
const gchar **argv;
|
|
va_list ap;
|
|
int i;
|
|
|
|
va_start (ap, argv1);
|
|
g_ptr_array_add (args, g_strdup (argv1));
|
|
while ((arg = va_arg (ap, const gchar *)))
|
|
{
|
|
if (arg == strv_arg)
|
|
{
|
|
argv = va_arg (ap, const gchar **);
|
|
if (argv != NULL)
|
|
{
|
|
for (i = 0; argv[i] != NULL; i++)
|
|
g_ptr_array_add (args, g_strdup (argv[i]));
|
|
}
|
|
}
|
|
else if (arg != skip_arg)
|
|
{
|
|
g_ptr_array_add (args, g_strdup (arg));
|
|
}
|
|
}
|
|
va_end (ap);
|
|
|
|
g_ptr_array_add (args, NULL);
|
|
|
|
if (!builder_maybe_host_spawnv (cwd_file, NULL, error, (const char * const *)args->pdata))
|
|
{
|
|
g_prefix_error (error, "module %s: ", module_name);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
builder_module_ensure_writable (BuilderModule *self,
|
|
BuilderCache *cache,
|
|
BuilderContext *context,
|
|
GError **error)
|
|
{
|
|
g_autoptr(GPtrArray) changes = NULL;
|
|
g_autoptr(GHashTable) matches = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
|
|
GFile *app_dir = builder_context_get_app_dir (context);
|
|
GHashTableIter iter;
|
|
gpointer key, value;
|
|
int i;
|
|
|
|
if (cache == NULL)
|
|
return TRUE;
|
|
|
|
if (self->ensure_writable == NULL ||
|
|
self->ensure_writable[0] == NULL)
|
|
return TRUE;
|
|
|
|
changes = builder_cache_get_files (cache, error);
|
|
if (changes == NULL)
|
|
return FALSE;
|
|
|
|
for (i = 0; i < changes->len; i++)
|
|
{
|
|
const char *path = g_ptr_array_index (changes, i);
|
|
const char *unprefixed_path;
|
|
const char *prefix;
|
|
|
|
if (g_str_has_prefix (path, "files/"))
|
|
prefix = "files/";
|
|
else if (g_str_has_prefix (path, "usr/"))
|
|
prefix = "usr/";
|
|
else
|
|
continue;
|
|
|
|
unprefixed_path = path + strlen (prefix);
|
|
|
|
collect_cleanup_for_path ((const char **)self->ensure_writable, unprefixed_path, prefix, matches);
|
|
}
|
|
|
|
g_hash_table_iter_init (&iter, matches);
|
|
while (g_hash_table_iter_next (&iter, &key, &value))
|
|
{
|
|
const char *path = key;
|
|
g_autoptr(GFile) dest = g_file_resolve_relative_path (app_dir, path);
|
|
|
|
g_debug ("Breaking hardlink %s\n", path);
|
|
if (!flatpak_break_hardlink (dest, error))
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
builder_module_build (BuilderModule *self,
|
|
BuilderCache *cache,
|
|
BuilderContext *context,
|
|
gboolean run_shell,
|
|
GError **error)
|
|
{
|
|
GFile *app_dir = builder_context_get_app_dir (context);
|
|
g_autofree char *make_j = NULL;
|
|
g_autofree char *make_l = NULL;
|
|
const char *make_cmd = NULL;
|
|
|
|
gboolean autotools = FALSE, cmake = FALSE, cmake_ninja = FALSE, meson = FALSE, simple = FALSE;
|
|
g_autoptr(GFile) configure_file = NULL;
|
|
GFile *build_parent_dir = NULL;
|
|
g_autoptr(GFile) build_dir = NULL;
|
|
g_autoptr(GFile) build_link = NULL;
|
|
g_autofree char *build_dir_relative = NULL;
|
|
gboolean has_configure;
|
|
gboolean var_require_builddir;
|
|
gboolean use_builddir;
|
|
int i;
|
|
g_auto(GStrv) env = NULL;
|
|
g_auto(GStrv) build_args = NULL;
|
|
g_auto(GStrv) config_opts = NULL;
|
|
g_autoptr(GFile) source_dir = NULL;
|
|
g_autoptr(GFile) source_subdir = NULL;
|
|
const char *source_subdir_relative = NULL;
|
|
g_autofree char *source_dir_path = NULL;
|
|
g_autofree char *buildname = NULL;
|
|
g_autoptr(GError) my_error = NULL;
|
|
BuilderPostProcessFlags post_process_flags = 0;
|
|
|
|
source_dir = builder_context_allocate_build_subdir (context, self->name, error);
|
|
if (source_dir == NULL)
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
|
|
build_parent_dir = g_file_get_parent (source_dir);
|
|
buildname = g_file_get_basename (source_dir);
|
|
source_dir_path = g_file_get_path (source_dir);
|
|
|
|
/* Make an unversioned symlink */
|
|
build_link = g_file_get_child (build_parent_dir, self->name);
|
|
if (!g_file_delete (build_link, NULL, &my_error) &&
|
|
!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
|
|
{
|
|
g_propagate_error (error, g_steal_pointer (&my_error));
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
g_clear_error (&my_error);
|
|
|
|
if (!g_file_make_symbolic_link (build_link,
|
|
buildname,
|
|
NULL, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
|
|
g_print ("========================================================================\n");
|
|
g_print ("Building module %s in %s\n", self->name, source_dir_path);
|
|
g_print ("========================================================================\n");
|
|
|
|
if (!builder_module_extract_sources (self, source_dir, context, error))
|
|
return FALSE;
|
|
|
|
if (self->subdir != NULL && self->subdir[0] != 0)
|
|
{
|
|
source_subdir = g_file_resolve_relative_path (source_dir, self->subdir);
|
|
source_subdir_relative = self->subdir;
|
|
}
|
|
else
|
|
{
|
|
source_subdir = g_object_ref (source_dir);
|
|
}
|
|
|
|
build_args = builder_options_get_build_args (self->build_options, context, error);
|
|
if (build_args == NULL)
|
|
return FALSE;
|
|
|
|
env = builder_options_get_env (self->build_options, context);
|
|
config_opts = builder_options_get_config_opts (self->build_options, context, self->config_opts);
|
|
|
|
if (!self->buildsystem)
|
|
{
|
|
if (self->cmake)
|
|
cmake = TRUE;
|
|
else
|
|
autotools = TRUE;
|
|
}
|
|
else if (!strcmp (self->buildsystem, "cmake"))
|
|
cmake = TRUE;
|
|
else if (!strcmp (self->buildsystem, "meson"))
|
|
meson = TRUE;
|
|
else if (!strcmp (self->buildsystem, "autotools"))
|
|
autotools = TRUE;
|
|
else if (!strcmp (self->buildsystem, "cmake-ninja"))
|
|
cmake_ninja = TRUE;
|
|
else if (!strcmp (self->buildsystem, "simple"))
|
|
simple = TRUE;
|
|
else
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "module %s: Invalid buildsystem: \"%s\"",
|
|
self->name, self->buildsystem);
|
|
return FALSE;
|
|
}
|
|
|
|
if (simple)
|
|
{
|
|
if (!self->build_commands)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "module %s: Buildsystem simple requires specifying \"build-commands\"",
|
|
self->name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (cmake || cmake_ninja)
|
|
{
|
|
g_autoptr(GFile) cmake_file = NULL;
|
|
|
|
cmake_file = g_file_get_child (source_subdir, "CMakeLists.txt");
|
|
if (!g_file_query_exists (cmake_file, NULL))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "module: %s: Can't find CMakeLists.txt", self->name);
|
|
return FALSE;
|
|
}
|
|
configure_file = g_object_ref (cmake_file);
|
|
}
|
|
else if (meson)
|
|
{
|
|
g_autoptr(GFile) meson_file = NULL;
|
|
|
|
meson_file = g_file_get_child (source_subdir, "meson.build");
|
|
if (!g_file_query_exists (meson_file, NULL))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "module: %s: Can't find meson.build", self->name);
|
|
return FALSE;
|
|
}
|
|
configure_file = g_object_ref (meson_file);
|
|
}
|
|
else if (autotools)
|
|
{
|
|
configure_file = g_file_get_child (source_subdir, "configure");
|
|
|
|
if (self->rm_configure)
|
|
{
|
|
if (!g_file_delete (configure_file, NULL, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (configure_file)
|
|
has_configure = g_file_query_exists (configure_file, NULL);
|
|
|
|
if (configure_file && !has_configure && !self->no_autogen)
|
|
{
|
|
const char *autogen_names[] = {"autogen", "autogen.sh", "bootstrap", "bootstrap.sh", NULL};
|
|
g_autofree char *autogen_cmd = NULL;
|
|
g_auto(GStrv) env_with_noconfigure = NULL;
|
|
|
|
for (i = 0; autogen_names[i] != NULL; i++)
|
|
{
|
|
g_autoptr(GFile) autogen_file = g_file_get_child (source_subdir, autogen_names[i]);
|
|
if (g_file_query_exists (autogen_file, NULL))
|
|
{
|
|
autogen_cmd = g_strdup_printf ("./%s", autogen_names[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (autogen_cmd == NULL)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "module %s: Can't find autogen, autogen.sh or bootstrap", self->name);
|
|
return FALSE;
|
|
}
|
|
|
|
env_with_noconfigure = g_environ_setenv (g_strdupv (env), "NOCONFIGURE", "1", TRUE);
|
|
if (!build (app_dir, self->name, context, source_dir, source_subdir_relative, build_args, env_with_noconfigure, error,
|
|
autogen_cmd, NULL))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!g_file_query_exists (configure_file, NULL))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "module %s: autogen did not create configure", self->name);
|
|
return FALSE;
|
|
}
|
|
|
|
has_configure = TRUE;
|
|
}
|
|
|
|
if (configure_file && has_configure)
|
|
{
|
|
const char *configure_cmd;
|
|
const char *cmake_generator = NULL;
|
|
gchar *configure_final_arg = NULL;
|
|
g_autofree char *configure_content = NULL;
|
|
g_auto(GStrv) configure_args = NULL;
|
|
g_autoptr(GPtrArray) configure_args_arr = g_ptr_array_new ();
|
|
|
|
if (!g_file_load_contents (configure_file, NULL, &configure_content, NULL, NULL, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
|
|
var_require_builddir = strstr (configure_content, "buildapi-variable-require-builddir") != NULL;
|
|
use_builddir = var_require_builddir || self->builddir;
|
|
|
|
if (use_builddir)
|
|
{
|
|
if (source_subdir_relative)
|
|
build_dir_relative = g_build_filename (source_subdir_relative, "_flatpak_build", NULL);
|
|
else
|
|
build_dir_relative = g_strdup ("_flatpak_build");
|
|
build_dir = g_file_get_child (source_subdir, "_flatpak_build");
|
|
|
|
if (!g_file_make_directory (build_dir, NULL, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
|
|
if (cmake || cmake_ninja)
|
|
{
|
|
configure_cmd = "cmake";
|
|
configure_final_arg = g_strdup("..");
|
|
}
|
|
else if (meson)
|
|
{
|
|
configure_cmd = "meson";
|
|
configure_final_arg = g_strdup ("..");
|
|
}
|
|
else
|
|
{
|
|
configure_cmd = "../configure";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
build_dir_relative = g_strdup (source_subdir_relative);
|
|
build_dir = g_object_ref (source_subdir);
|
|
if (cmake || cmake_ninja)
|
|
{
|
|
configure_cmd = "cmake";
|
|
configure_final_arg = g_strdup (".");
|
|
}
|
|
else if (meson)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "module %s: Meson does not support building in sourcedir, set \"builddir\" to true.", self->name);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
configure_cmd = "./configure";
|
|
}
|
|
}
|
|
|
|
if (cmake)
|
|
cmake_generator = "Unix Makefiles";
|
|
else if (cmake_ninja)
|
|
cmake_generator = "Ninja";
|
|
|
|
if (cmake || cmake_ninja)
|
|
{
|
|
g_ptr_array_add (configure_args_arr, g_strdup_printf ("-DCMAKE_INSTALL_PREFIX:PATH='%s'",
|
|
builder_options_get_prefix (self->build_options, context)));
|
|
g_ptr_array_add (configure_args_arr, g_strdup ("-G"));
|
|
g_ptr_array_add (configure_args_arr, g_strdup_printf ("%s", cmake_generator));
|
|
}
|
|
else /* autotools and meson */
|
|
{
|
|
g_ptr_array_add (configure_args_arr, g_strdup_printf ("--prefix=%s",
|
|
builder_options_get_prefix (self->build_options, context)));
|
|
}
|
|
|
|
g_ptr_array_add (configure_args_arr, configure_final_arg);
|
|
g_ptr_array_add (configure_args_arr, NULL);
|
|
|
|
configure_args = (char **) g_ptr_array_free (g_steal_pointer (&configure_args_arr), FALSE);
|
|
|
|
if (!build (app_dir, self->name, context, source_dir, build_dir_relative, build_args, env, error,
|
|
configure_cmd, strv_arg, configure_args, strv_arg, config_opts, NULL))
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
build_dir_relative = g_strdup (source_subdir_relative);
|
|
build_dir = g_object_ref (source_subdir);
|
|
}
|
|
|
|
if (meson || cmake_ninja)
|
|
{
|
|
g_autoptr(GFile) ninja_file = g_file_get_child (build_dir, "build.ninja");
|
|
if (!g_file_query_exists (ninja_file, NULL))
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "module %s: Can't find ninja file", self->name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (autotools || cmake)
|
|
{
|
|
const char *makefile_names[] = {"Makefile", "makefile", "GNUmakefile", NULL};
|
|
|
|
for (i = 0; makefile_names[i] != NULL; i++)
|
|
{
|
|
g_autoptr(GFile) makefile_file = g_file_get_child (build_dir, makefile_names[i]);
|
|
if (g_file_query_exists (makefile_file, NULL))
|
|
break;
|
|
}
|
|
|
|
if (makefile_names[i] == NULL)
|
|
{
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "module %s: Can't find makefile", self->name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!self->no_parallel_make)
|
|
{
|
|
make_j = g_strdup_printf ("-j%d", builder_context_get_jobs (context));
|
|
make_l = g_strdup_printf ("-l%d", 2 * builder_context_get_jobs (context));
|
|
}
|
|
|
|
if (run_shell)
|
|
{
|
|
if (!shell (app_dir, self->name, context, source_dir, build_dir_relative, build_args, env, error))
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Build and install */
|
|
|
|
if (meson || cmake_ninja)
|
|
make_cmd = "ninja";
|
|
else if (simple)
|
|
make_cmd = NULL;
|
|
else
|
|
make_cmd = "make";
|
|
|
|
if (make_cmd)
|
|
{
|
|
if (!build (app_dir, self->name, context, source_dir, build_dir_relative, build_args, env, error,
|
|
make_cmd, make_j ? make_j : skip_arg, make_l ? make_l : skip_arg, strv_arg, self->make_args, NULL))
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; self->build_commands != NULL && self->build_commands[i] != NULL; i++)
|
|
{
|
|
g_print ("Running: %s\n", self->build_commands[i]);
|
|
if (!build (app_dir, self->name, context, source_dir, build_dir_relative, build_args, env, error,
|
|
"/bin/sh", "-c", self->build_commands[i], NULL))
|
|
return FALSE;
|
|
}
|
|
|
|
if (!self->no_make_install && make_cmd)
|
|
{
|
|
if (!build (app_dir, self->name, context, source_dir, build_dir_relative, build_args, env, error,
|
|
make_cmd, "install", strv_arg, self->make_install_args, NULL))
|
|
return FALSE;
|
|
}
|
|
|
|
/* Post installation scripts */
|
|
|
|
if (builder_context_get_separate_locales (context))
|
|
{
|
|
g_autoptr(GFile) root_dir = NULL;
|
|
|
|
if (builder_context_get_build_runtime (context))
|
|
root_dir = g_file_get_child (app_dir, "usr");
|
|
else
|
|
root_dir = g_file_get_child (app_dir, "files");
|
|
|
|
if (!builder_migrate_locale_dirs (root_dir, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (self->post_install)
|
|
{
|
|
for (i = 0; self->post_install[i] != NULL; i++)
|
|
{
|
|
if (!build (app_dir, self->name, context, source_dir, build_dir_relative, build_args, env, error,
|
|
"/bin/sh", "-c", self->post_install[i], NULL))
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!self->no_python_timestamp_fix)
|
|
post_process_flags |= BUILDER_POST_PROCESS_FLAGS_PYTHON_TIMESTAMPS;
|
|
|
|
if (builder_options_get_strip (self->build_options, context))
|
|
post_process_flags |= BUILDER_POST_PROCESS_FLAGS_STRIP;
|
|
else if (!builder_options_get_no_debuginfo (self->build_options, context) &&
|
|
/* No support for debuginfo for extensions atm */
|
|
!builder_context_get_build_extension (context))
|
|
post_process_flags |= BUILDER_POST_PROCESS_FLAGS_DEBUGINFO;
|
|
|
|
if (!builder_post_process (post_process_flags, app_dir,
|
|
cache, context, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Clean up build dir */
|
|
|
|
if (!builder_context_get_keep_build_dirs (context))
|
|
{
|
|
if (!g_file_delete (build_link, NULL, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!flatpak_rm_rf (source_dir, NULL, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
builder_module_update (BuilderModule *self,
|
|
BuilderContext *context,
|
|
GError **error)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = self->sources; l != NULL; l = l->next)
|
|
{
|
|
BuilderSource *source = l->data;
|
|
|
|
if (!builder_source_is_enabled (source, context))
|
|
continue;
|
|
|
|
if (!builder_source_update (source, context, error))
|
|
{
|
|
g_prefix_error (error, "module %s: ", self->name);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
builder_module_checksum (BuilderModule *self,
|
|
BuilderCache *cache,
|
|
BuilderContext *context)
|
|
{
|
|
GList *l;
|
|
|
|
builder_cache_checksum_str (cache, BUILDER_MODULE_CHECKSUM_VERSION);
|
|
builder_cache_checksum_str (cache, self->name);
|
|
builder_cache_checksum_str (cache, self->subdir);
|
|
builder_cache_checksum_strv (cache, self->post_install);
|
|
builder_cache_checksum_strv (cache, self->config_opts);
|
|
builder_cache_checksum_strv (cache, self->make_args);
|
|
builder_cache_checksum_strv (cache, self->make_install_args);
|
|
builder_cache_checksum_strv (cache, self->ensure_writable);
|
|
builder_cache_checksum_strv (cache, self->only_arches);
|
|
builder_cache_checksum_strv (cache, self->skip_arches);
|
|
builder_cache_checksum_boolean (cache, self->rm_configure);
|
|
builder_cache_checksum_boolean (cache, self->no_autogen);
|
|
builder_cache_checksum_boolean (cache, self->disabled);
|
|
builder_cache_checksum_boolean (cache, self->no_parallel_make);
|
|
builder_cache_checksum_boolean (cache, self->no_make_install);
|
|
builder_cache_checksum_boolean (cache, self->no_python_timestamp_fix);
|
|
builder_cache_checksum_boolean (cache, self->cmake);
|
|
builder_cache_checksum_boolean (cache, self->builddir);
|
|
builder_cache_checksum_compat_strv (cache, self->build_commands);
|
|
|
|
if (self->build_options)
|
|
builder_options_checksum (self->build_options, cache, context);
|
|
|
|
for (l = self->sources; l != NULL; l = l->next)
|
|
{
|
|
BuilderSource *source = l->data;
|
|
|
|
if (!builder_source_is_enabled (source, context))
|
|
continue;
|
|
|
|
builder_source_checksum (source, cache, context);
|
|
}
|
|
}
|
|
|
|
void
|
|
builder_module_checksum_for_cleanup (BuilderModule *self,
|
|
BuilderCache *cache,
|
|
BuilderContext *context)
|
|
{
|
|
builder_cache_checksum_str (cache, BUILDER_MODULE_CHECKSUM_VERSION);
|
|
builder_cache_checksum_str (cache, self->name);
|
|
builder_cache_checksum_strv (cache, self->cleanup);
|
|
}
|
|
|
|
void
|
|
builder_module_checksum_for_platform (BuilderModule *self,
|
|
BuilderCache *cache,
|
|
BuilderContext *context)
|
|
{
|
|
builder_cache_checksum_strv (cache, self->cleanup_platform);
|
|
}
|
|
|
|
void
|
|
builder_module_set_json_path (BuilderModule *self,
|
|
const char *json_path)
|
|
{
|
|
self->json_path = g_strdup (json_path);
|
|
}
|
|
|
|
void
|
|
builder_module_set_base_dir (BuilderModule *self,
|
|
GFile* base_dir)
|
|
{
|
|
GList *l;
|
|
|
|
for (l = self->sources; l != NULL; l = l->next)
|
|
builder_source_set_base_dir (l->data, base_dir);
|
|
}
|
|
|
|
GPtrArray *
|
|
builder_module_get_changes (BuilderModule *self)
|
|
{
|
|
return self->changes;
|
|
}
|
|
|
|
void
|
|
builder_module_set_changes (BuilderModule *self,
|
|
GPtrArray *changes)
|
|
{
|
|
if (self->changes != changes)
|
|
{
|
|
if (self->changes)
|
|
g_ptr_array_unref (self->changes);
|
|
self->changes = g_ptr_array_ref (changes);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
matches_cleanup_for_path (const char **patterns,
|
|
const char *path)
|
|
{
|
|
int i;
|
|
|
|
if (patterns == NULL)
|
|
return FALSE;
|
|
|
|
for (i = 0; patterns[i] != NULL; i++)
|
|
{
|
|
if (flatpak_matches_path_pattern (path, patterns[i]))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
builder_module_cleanup_collect (BuilderModule *self,
|
|
gboolean platform,
|
|
BuilderContext *context,
|
|
GHashTable *to_remove_ht)
|
|
{
|
|
GPtrArray *changed_files;
|
|
int i;
|
|
const char **global_patterns;
|
|
const char **local_patterns;
|
|
|
|
if (!self->changes)
|
|
return;
|
|
|
|
if (platform)
|
|
{
|
|
global_patterns = builder_context_get_global_cleanup_platform (context);
|
|
local_patterns = (const char **) self->cleanup_platform;
|
|
}
|
|
else
|
|
{
|
|
global_patterns = builder_context_get_global_cleanup (context);
|
|
local_patterns = (const char **) self->cleanup;
|
|
}
|
|
|
|
changed_files = self->changes;
|
|
for (i = 0; i < changed_files->len; i++)
|
|
{
|
|
const char *path = g_ptr_array_index (changed_files, i);
|
|
const char *unprefixed_path;
|
|
const char *prefix;
|
|
|
|
if (g_str_has_prefix (path, "files/"))
|
|
prefix = "files/";
|
|
else if (g_str_has_prefix (path, "usr/"))
|
|
prefix = "usr/";
|
|
else
|
|
continue;
|
|
|
|
unprefixed_path = path + strlen (prefix);
|
|
|
|
collect_cleanup_for_path (global_patterns, unprefixed_path, prefix, to_remove_ht);
|
|
collect_cleanup_for_path (local_patterns, unprefixed_path, prefix, to_remove_ht);
|
|
|
|
if (g_str_has_prefix (unprefixed_path, "lib/debug/") &&
|
|
g_str_has_suffix (unprefixed_path, ".debug"))
|
|
{
|
|
g_autofree char *real_path = g_strdup (unprefixed_path);
|
|
g_autofree char *real_parent = NULL;
|
|
g_autofree char *parent = NULL;
|
|
g_autofree char *debug_path = NULL;
|
|
|
|
debug_path = g_strdup (unprefixed_path + strlen ("lib/debug/"));
|
|
debug_path[strlen (debug_path) - strlen (".debug")] = 0;
|
|
|
|
while (TRUE)
|
|
{
|
|
if (matches_cleanup_for_path (global_patterns, debug_path) ||
|
|
matches_cleanup_for_path (local_patterns, debug_path))
|
|
g_hash_table_insert (to_remove_ht, g_strconcat (prefix, real_path, NULL), GINT_TO_POINTER (1));
|
|
|
|
real_parent = g_path_get_dirname (real_path);
|
|
if (strcmp (real_parent, ".") == 0)
|
|
break;
|
|
g_free (real_path);
|
|
real_path = g_steal_pointer (&real_parent);
|
|
|
|
parent = g_path_get_dirname (debug_path);
|
|
g_free (debug_path);
|
|
debug_path = g_steal_pointer (&parent);
|
|
}
|
|
}
|
|
}
|
|
}
|