Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,4 @@ system-helper/org.freedesktop.Flatpak.rules
flatpak-bwrap
py-compile
libglnx-config.h
subprojects/.wraplock
12 changes: 12 additions & 0 deletions doc/flatpak-builder.xml
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,18 @@
</para></listitem>
</varlistentry>

<varlistentry>
<term><option>--compose-image-format=FORMAT</option></term>

<listitem><para>
Set the AppStream compose image format used for
generated icons and screenshots. Accepted values
are <literal>png</literal> and <literal>jxl</literal>.
<literal>jxl</literal> requires AppStream version >= 1.2.0.
Defaults to <literal>png</literal> if unspecified.
</para></listitem>
</varlistentry>

<varlistentry>
<term><option>--add-tag=TAG</option></term>

Expand Down
14 changes: 14 additions & 0 deletions src/builder-context.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ struct BuilderContext

BuilderAsUrlPolicy as_url_policy;
BuilderSourceDateEpochMode source_date_epoch_mode;
BuilderAsImageFormat as_image_format;
};

typedef struct
Expand Down Expand Up @@ -1310,6 +1311,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,
Expand Down
8 changes: 8 additions & 0 deletions src/builder-context.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ typedef enum {
BUILDER_SOURCE_DATE_EPOCH_UNSET,
} BuilderSourceDateEpochMode;

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 */

Expand Down Expand Up @@ -213,6 +217,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
Expand Down
24 changes: 24 additions & 0 deletions src/builder-main.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ static gboolean opt_log_system_bus;
static gboolean opt_yes;
static gint64 opt_source_date_epoch = SOURCE_DATE_EPOCH_DEFAULT;
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 },
Expand Down Expand Up @@ -151,6 +152,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 }
};

Expand Down Expand Up @@ -641,6 +643,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))
Expand Down
21 changes: 20 additions & 1 deletion src/builder-manifest.c
Original file line number Diff line number Diff line change
Expand Up @@ -2436,6 +2436,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;
Expand All @@ -2449,7 +2450,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);
Expand Down Expand Up @@ -3100,6 +3116,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)
{
Expand All @@ -3112,6 +3129,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,
Expand All @@ -3129,6 +3147,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,
Expand Down
8 changes: 8 additions & 0 deletions tests/libtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ assert_file_has_content () {
fi
}

assert_file_has_content_extended () {
if ! grep -q -E -e "$2" "$1"; then
sed -e 's/^/# /' < "$1" >&2
echo 1>&2 "File '$1' doesn't match extended regexp '$2'"
exit 1
fi
}

assert_symlink_has_content () {
if ! readlink "$1" | grep -q -e "$2"; then
readlink "$1" |sed -e 's/^/# /' >&2
Expand Down
1 change: 1 addition & 0 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ test_names = [
'test-builder-locale-migration',
'test-build-subj',
'test-sandboxed-mode-source-resolution',
'test-appstream',
]

tap_test = find_program(
Expand Down
90 changes: 90 additions & 0 deletions tests/test-appstream.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/bin/bash
#
# Copyright (C) 2026 Boudhayan Bhattacharya <bbhtt@bbhtt.in>
#
# 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 \
> appstream.xml
assert_file_has_content_extended appstream.xml '>org/flatpak/appstream_media/[^/]+/icons/128x128/org.flatpak.appstream_media.png</icon>'

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 \
> appstream.xml
assert_file_has_content_extended appstream.xml '>https://example.org/media/org/flatpak/appstream_media/[^/]+/icons/128x128/org.flatpak.appstream_media.png</icon>'

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 \
> appstream.xml
assert_file_has_content_extended appstream.xml '>https://example.org/media/org/flatpak/appstream_media/[^/]+/icons/128x128/org.flatpak.appstream_media.jxl</icon>'

echo "ok compose jxl image format"
else
echo "ok # Skip AppStream < 1.2.0"
fi
35 changes: 1 addition & 34 deletions tests/test-builder.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ set -euo pipefail

skip_without_fuse

echo "1..13"
echo "1..11"

setup_repo
install_repo
Expand All @@ -50,12 +50,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
Expand Down Expand Up @@ -168,35 +164,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</icon>'

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</icon>'

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

Expand Down