diff --git a/.gitignore b/.gitignore index 2930b0b6..1d08c4f0 100644 --- a/.gitignore +++ b/.gitignore @@ -104,3 +104,4 @@ system-helper/org.freedesktop.Flatpak.rules flatpak-bwrap py-compile libglnx-config.h +subprojects/.wraplock diff --git a/doc/flatpak-builder.xml b/doc/flatpak-builder.xml index f0136d41..5dcd513a 100644 --- a/doc/flatpak-builder.xml +++ b/doc/flatpak-builder.xml @@ -624,6 +624,18 @@ + + + + + Set the AppStream compose image format used for + generated icons and screenshots. Accepted values + are png and jxl. + jxl requires AppStream version >= 1.2.0. + Defaults to png if unspecified. + + + diff --git a/src/builder-context.c b/src/builder-context.c index d2153b83..4b130a94 100644 --- a/src/builder-context.c +++ b/src/builder-context.c @@ -90,6 +90,7 @@ struct BuilderContext BuilderSdkConfig *sdk_config; BuilderAsUrlPolicy as_url_policy; + BuilderAsImageFormat as_image_format; }; typedef struct @@ -1257,6 +1258,19 @@ builder_context_get_as_url_policy (BuilderContext *self) return self->as_url_policy; } +void +builder_context_set_as_image_format (BuilderContext *self, + BuilderAsImageFormat format) +{ + self->as_image_format = format; +} + +BuilderAsImageFormat +builder_context_get_as_image_format (BuilderContext *self) +{ + return self->as_image_format; +} + gboolean builder_context_resolve_source_uri (BuilderContext *self, const char *url, diff --git a/src/builder-context.h b/src/builder-context.h index 21a9132c..b58f3a62 100644 --- a/src/builder-context.h +++ b/src/builder-context.h @@ -34,6 +34,11 @@ typedef enum { BUILDER_AS_URL_POLICY_FULL, } BuilderAsUrlPolicy; +typedef enum { + BUILDER_AS_IMAGE_FORMAT_PNG = 0, + BUILDER_AS_IMAGE_FORMAT_JXL, +} BuilderAsImageFormat; + /* Same as SOUP_HTTP_URI_FLAGS, means all possible flags for http uris */ #if GLIB_CHECK_VERSION (2, 68, 0) @@ -202,6 +207,10 @@ void builder_context_set_as_url_policy (BuilderContext *self, BuilderAsUrlPolicy policy); BuilderAsUrlPolicy builder_context_get_as_url_policy (BuilderContext *self); +void builder_context_set_as_image_format (BuilderContext *self, + BuilderAsImageFormat format); +BuilderAsImageFormat builder_context_get_as_image_format (BuilderContext *self); + G_DEFINE_AUTOPTR_CLEANUP_FUNC (BuilderContext, g_object_unref) G_END_DECLS diff --git a/src/builder-main.c b/src/builder-main.c index 9602103d..a627f3ee 100644 --- a/src/builder-main.c +++ b/src/builder-main.c @@ -90,6 +90,7 @@ static gboolean opt_log_system_bus; static gboolean opt_yes; static gint64 opt_source_date_epoch = -1; static gchar *opt_as_url_policy = NULL; +static gchar *opt_as_image_format = NULL; static GOptionEntry entries[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &opt_verbose, "Print debug information during command processing", NULL }, @@ -146,6 +147,7 @@ static GOptionEntry entries[] = { { "no-shallow-clone", 0, 0, G_OPTION_ARG_NONE, &opt_no_shallow_clone, "Don't use shallow clones when mirroring git repos", NULL }, { "override-source-date-epoch", 0, 0, G_OPTION_ARG_INT64, &opt_source_date_epoch, "Use this timestamp to perform the build, instead of the last modification time of the manifest.", NULL }, { "compose-url-policy", 0, 0, G_OPTION_ARG_STRING, &opt_as_url_policy, "Set the AppStream compose URL policy to either 'partial' (default) or 'full'", "POLICY" }, + { "compose-image-format", 0, 0, G_OPTION_ARG_STRING, &opt_as_image_format, "Set the AppStream compose image format to either 'png' (default) or 'jxl'", "FORMAT" }, { NULL } }; @@ -636,6 +638,28 @@ main (int argc, builder_context_set_as_url_policy (build_context, policy); } + { + BuilderAsImageFormat image_format = BUILDER_AS_IMAGE_FORMAT_PNG; + + if (opt_as_image_format == NULL || g_strcmp0 (opt_as_image_format, "png") == 0) + image_format = BUILDER_AS_IMAGE_FORMAT_PNG; + else if (g_strcmp0 (opt_as_image_format, "jxl") == 0) + image_format = BUILDER_AS_IMAGE_FORMAT_JXL; + else + { + g_printerr ("Invalid value for --compose-image-format: %s\n", opt_as_image_format); + return 1; + } + + if (image_format == BUILDER_AS_IMAGE_FORMAT_JXL && !appstream_has_version (1, 2, 0)) + { + g_printerr ("AppStream version >= 1.2.0 required for 'jxl' compose image format\n"); + return 1; + } + + builder_context_set_as_image_format (build_context, image_format); + } + git_init_email (); if (!builder_context_create_state_dir (build_context, &error)) diff --git a/src/builder-manifest.c b/src/builder-manifest.c index 352f88c5..776a0d8b 100644 --- a/src/builder-manifest.c +++ b/src/builder-manifest.c @@ -2428,6 +2428,7 @@ cmpstringp (const void *p1, const void *p2) static gboolean appstreamcli_compose (GError **error, BuilderAsUrlPolicy as_url_policy, + BuilderAsImageFormat as_image_format, ...) { g_autoptr(GPtrArray) args = NULL; @@ -2441,7 +2442,22 @@ appstreamcli_compose (GError **error, if (as_url_policy == BUILDER_AS_URL_POLICY_FULL) g_ptr_array_add (args, g_strdup ("--no-partial-urls")); - va_start (ap, as_url_policy); + /* For AppStream >= 1.2.0 we need to explicitly pass --image-format, + since JXL is now the default and we want to keep producing + PNG unless the user asked for JXL. */ + if (appstream_has_version (1, 2, 0)) + { + g_ptr_array_add (args, g_strdup ("--image-format")); + g_ptr_array_add (args, g_strdup (as_image_format == BUILDER_AS_IMAGE_FORMAT_JXL ? "jxl" : "png")); + } + else + { + /* For AppStream < 1.2.0, --image-format doesn't exist and PNG is + the only supported format. The JXL choice is gated in main + on having >= 1.2.0 */ + } + + va_start (ap, as_image_format); while ((arg = va_arg (ap, const gchar *))) g_ptr_array_add (args, g_strdup (arg)); g_ptr_array_add (args, NULL); @@ -3092,6 +3108,7 @@ builder_manifest_cleanup (BuilderManifest *self, const char *opt_mirror_screenshots_url = builder_context_get_opt_mirror_screenshots_url (context); gboolean opt_export_only = builder_context_get_opt_export_only (context); BuilderAsUrlPolicy as_url_policy = builder_context_get_as_url_policy (context); + BuilderAsImageFormat as_image_format = builder_context_get_as_image_format (context); if (opt_mirror_screenshots_url && !opt_export_only) { @@ -3104,6 +3121,7 @@ builder_manifest_cleanup (BuilderManifest *self, g_print ("Saving screenshots in %s\n", flatpak_file_get_path_cached (media_dir)); if (!appstreamcli_compose (error, as_url_policy, + as_image_format, "--prefix=/", origin, arg_base_url, @@ -3121,6 +3139,7 @@ builder_manifest_cleanup (BuilderManifest *self, g_print ("Running appstreamcli compose\n"); if (!appstreamcli_compose (error, as_url_policy, + as_image_format, "--prefix=/", origin, result_root_arg, diff --git a/tests/Makefile.am.inc b/tests/Makefile.am.inc index 86a03d0d..2a661a65 100644 --- a/tests/Makefile.am.inc +++ b/tests/Makefile.am.inc @@ -91,6 +91,7 @@ dist_test_scripts = \ tests/test-builder-licence-paths.sh \ tests/test-builder-locale-migration.sh \ tests/test-sandboxed-mode-source-resolution.sh \ + tests/test-appstream.sh \ $(NULL) @VALGRIND_CHECK_RULES@ diff --git a/tests/meson.build b/tests/meson.build index 66ae6dbc..784b8aca 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -15,6 +15,7 @@ test_names = [ 'test-builder-licence-paths', 'test-builder-locale-migration', 'test-sandboxed-mode-source-resolution', + 'test-appstream', ] tap_test = find_program( diff --git a/tests/test-appstream.sh b/tests/test-appstream.sh new file mode 100755 index 00000000..1244af28 --- /dev/null +++ b/tests/test-appstream.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# +# Copyright (C) 2026 Boudhayan Bhattacharya +# +# This library 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 library 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 library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +set -euo pipefail + +. $(dirname $0)/libtest.sh + +skip_without_fuse + +echo "1..3" + +setup_repo +install_repo +setup_sdk_repo +install_sdk_repo + +cd $TEST_DATA_DIR/ + +cp $(dirname $0)/org.flatpak_builder.gui.desktop . +cp $(dirname $0)/org.flatpak_builder.gui.metainfo.xml . +cp $(dirname $0)/org.test.Hello-256.png . +cp $(dirname $0)/org.flatpak.appstream_media.json . + +# test compose partial url policy +APPDIR=builddir_sc \ +run_build \ + --mirror-screenshots-url=https://example.org/media \ + --state-dir .fp-compose-url-policy-partial \ + --compose-url-policy=partial \ + org.flatpak.appstream_media.json +# we test for the icon tag instead of screenshot +# the former works offline the latter does not +gzip -cdq builddir_sc/files/share/app-info/xmls/org.flatpak.appstream_media.xml.gz|grep -Eq '>org/flatpak/appstream_media/[^/]+/icons/128x128/org.flatpak.appstream_media.png' + +echo "ok compose partial url policy" + +# test compose full url policy +if appstream_has_version 0 16 3; then + APPDIR=builddir_sc \ + run_build \ + --mirror-screenshots-url=https://example.org/media \ + --state-dir .fp-compose-url-policy-full \ + --compose-url-policy=full \ + org.flatpak.appstream_media.json + + gzip -cdq builddir_sc/files/share/app-info/xmls/org.flatpak.appstream_media.xml.gz|grep -Eq '>https://example.org/media/org/flatpak/appstream_media/[^/]+/icons/128x128/org.flatpak.appstream_media.png' + + echo "ok compose full url policy" +else + echo "ok # Skip AppStream < 0.16.3" +fi + +# test compose jxl image format +if appstream_has_version 1 2 0; then + APPDIR=builddir_jxl \ + run_build \ + --mirror-screenshots-url=https://example.org/media \ + --state-dir .fp-compose-image-format-jxl \ + --compose-url-policy=full \ + --compose-image-format=jxl \ + org.flatpak.appstream_media.json + + gzip -cdq builddir_jxl/files/share/app-info/xmls/org.flatpak.appstream_media.xml.gz|grep -Eq '>https://example.org/media/org/flatpak/appstream_media/[^/]+/icons/128x128/org.flatpak.appstream_media.jxl' + + echo "ok compose jxl image format" +else + echo "ok # Skip AppStream < 1.2.0" +fi diff --git a/tests/test-builder.sh b/tests/test-builder.sh index 3c8ef367..67dd5218 100755 --- a/tests/test-builder.sh +++ b/tests/test-builder.sh @@ -23,7 +23,7 @@ set -euo pipefail skip_without_fuse -echo "1..12" +echo "1..10" setup_repo install_repo @@ -49,12 +49,8 @@ cp $(dirname $0)/Hello-desktop.appdata.xml . cp $(dirname $0)/org.test.Hello.desktop . cp $(dirname $0)/org.test.Hello.xml . cp $(dirname $0)/org.test.Hello.appdata.xml . -cp $(dirname $0)/org.flatpak_builder.gui.desktop . cp $(dirname $0)/org.flatpak_builder.gui.json . -cp $(dirname $0)/org.flatpak_builder.gui.metainfo.xml . cp $(dirname $0)/org.test.Hello.png . -cp $(dirname $0)/org.test.Hello-256.png . -cp $(dirname $0)/org.flatpak.appstream_media.json . cp $(dirname $0)/org.flatpak.install_test.json . cp $(dirname $0)/test-locale-cleanup.json . mkdir include1 @@ -150,35 +146,6 @@ find outdir_sc -path "*/icons/64x64/org.test.Hello.png" -type f | grep -q . echo "ok screenshot ref commit" -# test compose partial url policy -APPDIR=builddir_sc \ -run_build \ - --mirror-screenshots-url=https://example.org/media \ - --state-dir .fp-compose-url-policy-partial \ - --compose-url-policy=partial \ - org.flatpak.appstream_media.json -# we test for the icon tag instead of screenshot -# the former works offline the latter does not -gzip -cdq builddir_sc/files/share/app-info/xmls/org.flatpak.appstream_media.xml.gz|grep -Eq '>org/flatpak/appstream_media/[^/]+/icons/128x128/org.flatpak.appstream_media.png' - -echo "ok compose partial url policy" - -# test compose full url policy -if appstream_has_version 0 16 3; then - APPDIR=builddir_sc \ - run_build \ - --mirror-screenshots-url=https://example.org/media \ - --state-dir .fp-compose-url-policy-full \ - --compose-url-policy=full \ - org.flatpak.appstream_media.json - - gzip -cdq builddir_sc/files/share/app-info/xmls/org.flatpak.appstream_media.xml.gz|grep -Eq '>https://example.org/media/org/flatpak/appstream_media/[^/]+/icons/128x128/org.flatpak.appstream_media.png' - - echo "ok compose full url policy" -else - echo "ok # Skip AppStream < 0.16.3" -fi - # test install APPDIR=builddir run_build --user --install org.flatpak.install_test.json