/* 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 #include "libglnx/libglnx.h" #include "builder-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 *install_rule; char *test_rule; 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; gboolean run_tests; BuilderOptions *build_options; GPtrArray *changes; char **cleanup; char **cleanup_platform; GList *sources; GList *modules; char **build_commands; char **test_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_INSTALL_RULE, PROP_TEST_RULE, PROP_BUILDSYSTEM, PROP_BUILDDIR, PROP_CONFIG_OPTS, PROP_MAKE_ARGS, PROP_MAKE_INSTALL_ARGS, PROP_ENSURE_WRITABLE, PROP_ONLY_ARCHES, PROP_RUN_TESTS, PROP_SKIP_ARCHES, PROP_SOURCES, PROP_BUILD_OPTIONS, PROP_CLEANUP, PROP_CLEANUP_PLATFORM, PROP_POST_INSTALL, PROP_MODULES, PROP_BUILD_COMMANDS, PROP_TEST_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->install_rule); g_free (self->test_rule); 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); g_strfreev (self->test_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_INSTALL_RULE: g_value_set_string (value, self->install_rule); break; case PROP_TEST_RULE: g_value_set_string (value, self->test_rule); 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; case PROP_TEST_COMMANDS: g_value_set_boxed (value, self->test_commands); break; case PROP_RUN_TESTS: g_value_set_boolean (value, self->run_tests); 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_INSTALL_RULE: g_free (self->install_rule); self->install_rule = g_value_dup_string (value); break; case PROP_TEST_RULE: g_free (self->test_rule); self->test_rule = 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; case PROP_TEST_COMMANDS: tmp = self->test_commands; self->test_commands = g_strdupv (g_value_get_boxed (value)); g_strfreev (tmp); break; case PROP_RUN_TESTS: self->run_tests = g_value_get_boolean (value); 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_INSTALL_RULE, g_param_spec_string ("install-rule", "", "", NULL, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_TEST_RULE, g_param_spec_string ("test-rule", "", "", 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)); g_object_class_install_property (object_class, PROP_TEST_COMMANDS, g_param_spec_boxed ("test-commands", "", "", G_TYPE_STRV, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_RUN_TESTS, g_param_spec_boolean ("run-tests", "", "", FALSE, 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 GList * load_sources_from_json (const char *sources_relpath) { g_autoptr(BuilderObjectList) sources = NULL; g_autoptr(GFile) saved_demarshal_base_dir = builder_manifest_get_demarshal_base_dir (); g_autoptr(GFile) sources_file = g_file_resolve_relative_path (saved_demarshal_base_dir, sources_relpath); g_autoptr(GFile) sources_file_dir = g_file_get_parent (sources_file); const char *sources_path = flatpak_file_get_path_cached (sources_file); g_autofree char *sources_json = NULL; g_autoptr(JsonNode) sources_root = NULL; g_autoptr(GError) error = NULL; BuilderSource *source; if (!g_file_get_contents (sources_path, &sources_json, NULL, NULL)) { g_printerr ("Can't open %s\n", sources_path); return NULL; } builder_manifest_set_demarshal_base_dir (sources_file_dir); sources_root = json_from_string (sources_json, &error); if (sources_root == NULL) { g_printerr ("Error parsing %s: %s\n", sources_relpath, error->message); return NULL; } if (JSON_NODE_TYPE (sources_root) == JSON_NODE_OBJECT) { source = builder_source_from_json (sources_root); if (source == NULL) return NULL; sources = g_list_prepend (sources, source); } else if (JSON_NODE_TYPE (sources_root) == JSON_NODE_ARRAY) { JsonArray *array = json_node_get_array (sources_root); guint i, array_len = json_array_get_length (array); for (i = 0; i < array_len; i++) { JsonNode *array_element_node = json_array_get_element (array, i); if (JSON_NODE_HOLDS_OBJECT (array_element_node)) { source = builder_source_from_json (array_element_node); if (source == NULL) return NULL; sources = g_list_prepend (sources, source); } else return NULL; } } else return NULL; return g_steal_pointer (&sources); } 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 *module_contents = NULL; if (g_file_get_contents (module_path, &module_contents, NULL, NULL)) { g_autoptr(GFile) module_file_dir = g_file_get_parent (module_file); builder_manifest_set_demarshal_base_dir (module_file_dir); module = builder_gobject_from_data (BUILDER_TYPE_MODULE, module_relpath, module_contents, 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); g_autoptr(BuilderObjectList) sources = NULL; BuilderSource *source; for (i = 0; i < array_len; i++) { JsonNode *element_node = json_array_get_element (array, i); if (JSON_NODE_HOLDS_VALUE (element_node) && json_node_get_value_type (element_node) == G_TYPE_STRING) { GList *new_sources = load_sources_from_json (json_node_get_string (element_node)); if (new_sources == NULL) return FALSE; sources = g_list_concat (new_sources, sources); } else if (JSON_NODE_TYPE (element_node) == JSON_NODE_OBJECT) { source = builder_source_from_json (element_node); if (source == NULL) return FALSE; sources = g_list_prepend (sources, source); } else return FALSE; } g_value_set_pointer (value, g_list_reverse (g_steal_pointer (&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_should_build (BuilderModule *self) { if (self->sources != NULL) return TRUE; /* We allow building simple types even without sources, that is often useful */ if (!g_strcmp0 (self->buildsystem, "simple")) return TRUE; return FALSE; } 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; builder_set_term_title (_("Downloading %s"), self->name); 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; } void builder_module_finish_sources (BuilderModule *self, GPtrArray *args, BuilderContext *context) { GList *l; for (l = self->sources; l != NULL; l = l->next) { BuilderSource *source = l->data; if (!builder_source_is_enabled (source, context)) continue; builder_source_finish (source, args, context); } } 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")); g_ptr_array_add (args, g_strdup ("--die-with-parent")); source_dir_path_canonical = realpath (source_dir_path, NULL); if (source_dir_path_canonical == NULL) source_dir_path_canonical = g_strdup (source_dir_path); 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, 0, 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", path); if (!flatpak_break_hardlink (dest, error)) return FALSE; } return TRUE; } static GFile * find_file_with_extension (GFile *dir, const char *extension) { g_autoptr(GFileEnumerator) dir_enum = NULL; GFileInfo *next = NULL; dir_enum = g_file_enumerate_children (dir, "standard::name,standard::type", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL); while (dir_enum != NULL && (next = g_file_enumerator_next_file (dir_enum, NULL, NULL))) { g_autoptr(GFileInfo) child_info = next; const char *name = g_file_info_get_name (child_info); if (g_str_has_suffix (name, extension)) return g_file_enumerator_get_child (dir_enum, child_info); } return NULL; } static gboolean builder_module_build_helper (BuilderModule *self, BuilderCache *cache, BuilderContext *context, GFile *source_dir, 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; const char *test_arg = NULL; gboolean autotools = FALSE, cmake = FALSE, cmake_ninja = FALSE, meson = FALSE, simple = FALSE, qmake = FALSE; g_autoptr(GFile) configure_file = NULL; g_autoptr(GFile) build_dir = NULL; g_autofree char *build_dir_relative = NULL; gboolean has_configure = FALSE; 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_subdir = NULL; const char *source_subdir_relative = NULL; g_autofree char *source_dir_path = NULL; BuilderPostProcessFlags post_process_flags = 0; source_dir_path = g_file_get_path (source_dir); g_print ("========================================================================\n"); g_print ("Building module %s in %s\n", self->name, source_dir_path); g_print ("========================================================================\n"); builder_set_term_title (_("Building %s"), self->name); 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 if (!strcmp (self->buildsystem, "qmake")) qmake = 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 (qmake) { configure_file = find_file_with_extension (source_subdir, ".pro"); if (configure_file == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "module: %s: Can't find *.pro file", self->name); return FALSE; } } 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_auto(GStrv) configure_args = NULL; g_autoptr(GPtrArray) configure_args_arr = g_ptr_array_new (); g_autofree char *configure_content = NULL; 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 || meson; 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 (qmake) { g_autofree char *basename = g_file_get_basename (configure_file); configure_cmd = "qmake"; configure_final_arg = g_strconcat ("../", basename, NULL); } 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 (qmake) { configure_cmd = "qmake"; configure_final_arg = g_file_get_basename (configure_file); } else { g_assert (!meson); 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 if (qmake) { g_ptr_array_add (configure_args_arr, g_strdup_printf ("PREFIX='%s'", builder_options_get_prefix (self->build_options, context))); } 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 || qmake) { 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 */ builder_set_term_title (_("Installing %s"), self->name); if (meson || cmake_ninja) { make_cmd = "ninja"; test_arg = "test"; } else if (simple) make_cmd = NULL; else { make_cmd = "make"; test_arg = "check"; } if (self->test_rule) test_arg = self->test_rule; if (make_cmd) { g_auto(GStrv) make_args = builder_options_get_make_args (self->build_options, context, self->make_args); 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, 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) { g_auto(GStrv) make_install_args = builder_options_get_make_install_args (self->build_options, context, self->make_install_args); if (!build (app_dir, self->name, context, source_dir, build_dir_relative, build_args, env, error, make_cmd, self->install_rule ? self->install_rule : "install", strv_arg, make_install_args, NULL)) return FALSE; } /* Post installation scripts */ builder_set_term_title (_("Post-Install %s"), self->name); 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; } } /* Run unit tests */ if (self->run_tests && builder_context_get_run_tests (context)) { g_auto(GStrv) test_args = NULL; builder_set_term_title (_("Testing %s"), self->name); g_print ("Running tests\n"); test_args = builder_options_get_test_args (self->build_options, context, error); if (test_args == NULL) return FALSE; if (make_cmd && test_arg && *test_arg != 0) { if (!build (app_dir, self->name, context, source_dir, build_dir_relative, test_args, env, error, make_cmd, test_arg, NULL)) { g_prefix_error (error, "Running %s %s failed: ", make_cmd, test_arg); return FALSE; } } for (i = 0; self->test_commands != NULL && self->test_commands[i] != NULL; i++) { g_print ("Running: %s\n", self->test_commands[i]); if (!build (app_dir, self->name, context, source_dir, build_dir_relative, test_args, env, error, "/bin/sh", "-c", self->test_commands[i], NULL)) { g_prefix_error (error, "Running test command '%s' failed: ", self->test_commands[i]); 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_options_get_no_debuginfo_compression (self->build_options, context)) post_process_flags |= BUILDER_POST_PROCESS_FLAGS_DEBUGINFO_COMPRESSION; } if (!builder_post_process (post_process_flags, app_dir, cache, context, error)) { g_prefix_error (error, "module %s: ", self->name); return FALSE; } return TRUE; } gboolean builder_module_build (BuilderModule *self, BuilderCache *cache, BuilderContext *context, gboolean run_shell, GError **error) { g_autoptr(GFile) source_dir = NULL; g_autoptr(GFile) build_parent_dir = NULL; g_autoptr(GFile) build_link = NULL; g_autoptr(GError) my_error = NULL; g_autofree char *buildname = NULL; gboolean res; 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); /* 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; } res = builder_module_build_helper (self, cache, context, source_dir, run_shell, error); /* Clean up build dir */ if (!run_shell && (!builder_context_get_keep_build_dirs (context) && (res || builder_context_get_delete_build_dirs (context)))) { builder_set_term_title (_("Cleanup %s"), self->name); 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 res; } 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; builder_set_term_title (_("Updating %s"), self->name); 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_strv (cache, self->build_commands); builder_cache_checksum_str (cache, self->buildsystem); builder_cache_checksum_str (cache, self->install_rule); builder_cache_checksum_compat_boolean (cache, self->run_tests); 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); } } } }