/* 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 . * * Authors: * Alexander Larsson */ #include "config.h" #include #include #include #include #include #include #include "libglnx/libglnx.h" #include "libgsystem.h" #include "xdg-app-utils.h" #include "builder-utils.h" #include "builder-module.h" struct BuilderModule { GObject parent; char *name; char *subdir; char **post_install; char **config_opts; char **make_args; char **make_install_args; gboolean rm_configure; gboolean no_autogen; gboolean cmake; gboolean builddir; BuilderOptions *build_options; GPtrArray *changes; char **cleanup; char **cleanup_platform; GList *sources; }; 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_NO_AUTOGEN, PROP_CMAKE, PROP_BUILDDIR, PROP_CONFIG_OPTS, PROP_MAKE_ARGS, PROP_MAKE_INSTALL_ARGS, PROP_SOURCES, PROP_BUILD_OPTIONS, PROP_CLEANUP, PROP_CLEANUP_PLATFORM, PROP_POST_INSTALL, LAST_PROP }; static void builder_module_finalize (GObject *object) { BuilderModule *self = (BuilderModule *)object; g_free (self->name); g_free (self->subdir); g_strfreev (self->post_install); g_strfreev (self->config_opts); g_strfreev (self->make_args); g_strfreev (self->make_install_args); g_clear_object (&self->build_options); g_list_free_full (self->sources, g_object_unref); g_strfreev (self->cleanup); g_strfreev (self->cleanup_platform); 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_NO_AUTOGEN: g_value_set_boolean (value, self->no_autogen); break; case PROP_CMAKE: g_value_set_boolean (value, self->cmake); 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_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; 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; switch (prop_id) { case PROP_NAME: g_clear_pointer (&self->name, g_free); self->name = g_value_dup_string (value); 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_NO_AUTOGEN: self->no_autogen = g_value_get_boolean (value); break; case PROP_CMAKE: self->cmake = g_value_get_boolean (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_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; 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_NO_AUTOGEN, g_param_spec_boolean ("no-autogen", "", "", FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_CMAKE, g_param_spec_boolean ("cmake", "", "", FALSE, 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_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)); } 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, "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, "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; } const char * builder_module_get_name (BuilderModule *self) { return self->name; } GList * builder_module_get_sources (BuilderModule *self) { return self->sources; } 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_download (source, update_vcs, context, error)) 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_extract (source, dest, context, error)) 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 **xdg_app_opts, char **env_vars, GError **error, const gchar *argv1, ...) { g_autoptr(GSubprocessLauncher) launcher = NULL; g_autoptr(GSubprocess) subp = NULL; g_autoptr(GPtrArray) args = NULL; const gchar *arg; const gchar **argv; g_autofree char *commandline = 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; va_list ap; int i; args = g_ptr_array_new_with_free_func (g_free); g_ptr_array_add (args, g_strdup ("xdg-app")); g_ptr_array_add (args, g_strdup ("build")); source_dir_path_canonical = canonicalize_file_name (source_dir_path); g_ptr_array_add (args, g_strdup ("--nofilesystem=host")); g_ptr_array_add (args, g_strdup_printf ("--filesystem=%s", source_dir_path_canonical)); g_ptr_array_add (args, g_strdup_printf ("--bind-mount=/run/build/%s=%s", module_name, source_dir_path_canonical)); if (cwd_subdir) g_ptr_array_add (args, g_strdup_printf ("--build-dir=/run/build/%s/%s", module_name, cwd_subdir)); else g_ptr_array_add (args, g_strdup_printf ("--build-dir=/run/build/%s", 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 (xdg_app_opts) { for (i = 0; xdg_app_opts[i] != NULL; i++) g_ptr_array_add (args, g_strdup (xdg_app_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)); 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)); } g_ptr_array_add (args, NULL); va_end (ap); commandline = g_strjoinv (" ", (char **) args->pdata); g_print ("Running: %s\n", commandline); launcher = g_subprocess_launcher_new (0); g_subprocess_launcher_set_cwd (launcher, source_dir_path_canonical); subp = g_subprocess_launcher_spawnv (launcher, (const gchar * const *) args->pdata, error); g_ptr_array_free (args, TRUE); if (subp == NULL || !g_subprocess_wait_check (subp, NULL, error)) return FALSE; return TRUE; } static gboolean builder_module_handle_debuginfo (BuilderModule *self, GFile *app_dir, BuilderCache *cache, BuilderContext *context, GError **error) { g_autofree char *app_dir_path = g_file_get_path (app_dir); int i; g_autoptr(GPtrArray) added = NULL; g_autoptr(GPtrArray) modified = NULL; g_autoptr(GPtrArray) added_or_modified = g_ptr_array_new (); if (!builder_cache_get_outstanding_changes (cache, &added, &modified, NULL, error)) return FALSE; for (i = 0; i < added->len; i++) g_ptr_array_add (added_or_modified, g_ptr_array_index (added, i)); for (i = 0; i < modified->len; i++) g_ptr_array_add (added_or_modified, g_ptr_array_index (modified, i)); g_ptr_array_sort (added_or_modified, xdg_app_strcmp0_ptr); for (i = 0; i < added_or_modified->len; i++) { const char *rel_path = (char *)g_ptr_array_index (added_or_modified, i); g_autoptr(GFile) file = g_file_resolve_relative_path (app_dir, rel_path); g_autofree char *path = g_file_get_path (file); g_autofree char *debug_path = NULL; g_autofree char *real_debug_path = NULL; gboolean is_shared, is_stripped; if (is_elf_file (path, &is_shared, &is_stripped)) { if (builder_options_get_strip (self->build_options, context)) { g_print ("stripping: %s\n", rel_path); if (is_shared) { if (!strip (error, "--remove-section=.comment", "--remove-section=.note", "--strip-unneeded", path, NULL)) return FALSE; } else { if (!strip (error, "--remove-section=.comment", "--remove-section=.note", path, NULL)) return FALSE; } } else if (!builder_options_get_no_debuginfo (self->build_options, context)) { g_autofree char *rel_path_dir = g_path_get_dirname (rel_path); g_autofree char *filename = g_path_get_basename (rel_path); g_autofree char *filename_debug = g_strconcat (filename, ".debug", NULL); g_autofree char *debug_dir = NULL; g_autofree char *real_debug_dir = NULL; if (g_str_has_prefix (rel_path_dir, "files/")) { debug_dir = g_build_filename (app_dir_path, "files/lib/debug", rel_path_dir + strlen("files/"), NULL); real_debug_dir = g_build_filename ("/app/lib/debug", rel_path_dir + strlen("files/"), NULL); } else if (g_str_has_prefix (rel_path_dir, "usr/")) { debug_dir = g_build_filename (app_dir_path, "usr/lib/debug", rel_path_dir, NULL); real_debug_dir = g_build_filename ("/usr/lib/debug", rel_path_dir, NULL); } if (debug_dir) { if (g_mkdir_with_parents (debug_dir, 0755) != 0) { glnx_set_error_from_errno (error); return FALSE; } debug_path = g_build_filename (debug_dir, filename_debug, NULL); real_debug_path = g_build_filename (real_debug_dir, filename_debug, NULL); g_print ("stripping %s to %s\n", path, debug_path); eu_strip (error, "--remove-comment", "--reloc-debug-sections", "-f", debug_path, "-F", real_debug_path, path, NULL); } } } } return TRUE; } gboolean builder_module_build (BuilderModule *self, BuilderCache *cache, BuilderContext *context, GError **error) { GFile *app_dir = builder_context_get_app_dir (context); g_autofree char *make_j = NULL; g_autofree char *make_l = NULL; g_autofree char *makefile_content = NULL; g_autoptr(GFile) configure_file = NULL; g_autoptr(GFile) cmake_file = NULL; const char *makefile_names[] = {"Makefile", "makefile", "GNUmakefile", NULL}; g_autoptr(GFile) build_dir = NULL; g_autofree char *build_dir_relative = NULL; gboolean has_configure; gboolean var_require_builddir; gboolean has_notparallel; gboolean use_builddir; int i; g_auto(GStrv) env = NULL; g_auto(GStrv) build_args = NULL; const char *cflags, *cxxflags; g_autofree char *buildname = NULL; g_autoptr(GFile) source_dir = NULL; g_autoptr(GFile) source_subdir = NULL; const char *source_subdir_relative = NULL; g_autoptr(GFile) source_dir_template = NULL; g_autofree char *source_dir_path = NULL; buildname = g_strdup_printf ("build-%s-XXXXXX", self->name); source_dir_template = g_file_get_child (builder_context_get_state_dir (context), buildname); source_dir_path = g_file_get_path (source_dir_template); if (g_mkdtemp (source_dir_path) == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't create build directory"); return FALSE; } source_dir = g_file_new_for_path (source_dir_path); 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); env = builder_options_get_env (self->build_options, context); build_args = builder_options_get_build_args (self->build_options, context); cflags = builder_options_get_cflags (self->build_options, context); if (cflags) env = g_environ_setenv (env, "CFLAGS", cflags, TRUE); cxxflags = builder_options_get_cxxflags (self->build_options, context); if (cxxflags) env = g_environ_setenv (env, "CXXFLAGS", cxxflags, TRUE); if (self->cmake) { 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, "Can't find CMakeLists.txt"); return FALSE; } configure_file = g_object_ref (cmake_file); } else { configure_file = g_file_get_child (source_subdir, "configure"); if (self->rm_configure) { if (!g_file_delete (configure_file, NULL, error)) return FALSE; } } has_configure = g_file_query_exists (configure_file, NULL); if (!has_configure && !self->no_autogen) { const char *autogen_names[] = {"autogen", "autogen.sh", "bootstrap", 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, "Can't find autogen, autogen.sh or bootstrap"); 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)) return FALSE; if (!g_file_query_exists (configure_file, NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "autogen did not create configure"); return FALSE; } has_configure = TRUE; } if (has_configure) { const char *configure_cmd; const char *configure_final_arg = skip_arg; g_autofree char *configure_prefix_arg = NULL; g_autofree char *configure_content = NULL; if (!g_file_load_contents (configure_file, NULL, &configure_content, NULL, NULL, error)) 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, "_xdg_app_build", NULL); else build_dir_relative = g_strdup ("_xdg_app_build"); build_dir = g_file_get_child (source_subdir, "_xdg_app_build"); if (!g_file_make_directory (build_dir, NULL, error)) return FALSE; if (self->cmake) { configure_cmd = "cmake"; configure_final_arg = ".."; } else { configure_cmd = "../configure"; } } else { build_dir_relative = g_strdup (source_subdir_relative); build_dir = g_object_ref (source_subdir); if (self->cmake) { configure_cmd = "cmake"; configure_final_arg = "."; } else configure_cmd = "./configure"; } if (self->cmake) configure_prefix_arg = g_strdup_printf ("-DCMAKE_INSTALL_PREFIX:PATH='%s'", builder_options_get_prefix (self->build_options, context)); else configure_prefix_arg = g_strdup_printf ("--prefix=%s", builder_options_get_prefix (self->build_options, context)); if (!build (app_dir, self->name, context, source_dir, build_dir_relative, build_args, env, error, configure_cmd, configure_prefix_arg, strv_arg, self->config_opts, configure_final_arg, NULL)) return FALSE; } else { build_dir_relative = g_strdup (source_subdir_relative); build_dir = g_object_ref (source_subdir); } 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)) { if (!g_file_load_contents (makefile_file, NULL, &makefile_content, NULL, NULL, error)) return FALSE; break; } } if (makefile_content == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Can't find makefile"); return FALSE; } has_notparallel = g_str_has_prefix (makefile_content, ".NOTPARALLEL") || (strstr (makefile_content, "\n.NOTPARALLEL") != NULL); if (!has_notparallel) { make_j = g_strdup_printf ("-j%d", builder_context_get_n_cpu (context)); make_l = g_strdup_printf ("-l%d", 2*builder_context_get_n_cpu (context)); } /* Build and install */ if (!build (app_dir, self->name, context, source_dir, build_dir_relative, build_args, env, error, "make", make_j?make_j:skip_arg, make_l?make_l:skip_arg, strv_arg, self->make_args, NULL)) return FALSE; if (!build (app_dir, self->name, context, source_dir, build_dir_relative, build_args, env, error, "make", "install", strv_arg, self->make_install_args, NULL)) return FALSE; /* Post installation scripts */ 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 (!builder_module_handle_debuginfo (self, app_dir, cache, context, error)) return FALSE; /* Clean up build dir */ if (builder_context_get_keep_build_dirs (context)) { g_autofree char *buildname_link = g_strdup_printf ("build-%s", self->name); g_autoptr(GFile) build_link = g_file_get_child (builder_context_get_state_dir (context), buildname_link); g_autoptr(GError) my_error = NULL; g_autofree char *buildname_target = g_path_get_basename (source_dir_path); 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)); return FALSE; } if (!g_file_make_symbolic_link (build_link, buildname_target, NULL, error)) return FALSE; } else { if (!gs_shutil_rm_rf (source_dir, NULL, error)) 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_update (source, context, error)) 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_boolean (cache, self->rm_configure); builder_cache_checksum_boolean (cache, self->no_autogen); builder_cache_checksum_boolean (cache, self->cmake); builder_cache_checksum_boolean (cache, self->builddir); 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; 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); } 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 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++) xdg_app_collect_matches_for_path_pattern (path, patterns[i], add_prefix, to_remove_ht); } 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 (xdg_app_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 (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); } } } }