Skip to content
Draft
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
3 changes: 2 additions & 1 deletion .github/dependencies.apt.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
appstream-compose
dbus
debugedit
desktop-file-utils
Expand All @@ -9,6 +8,8 @@ fuse3
gettext
git
git-lfs
libappstream-compose-dev
libappstream-dev
libarchive-tools
libcurl4-openssl-dev
libelf-dev
Expand Down
4 changes: 0 additions & 4 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@ endforeach
# The debugedit program is a hard dependency
debugedit = find_program('debugedit', version: '>= 5.0')

# Require appstream with compose plugin installed
appstreamcli = find_program('appstreamcli', version: '>= 0.15.0')
appstreamcli_compose = run_command(appstreamcli, ['compose', '--help'], check: true)

fusermount = get_option('system_fusermount')
if fusermount == ''
fusermount_program = find_program(['fusermount3', 'fusermount'], required: true)
Expand Down
20 changes: 20 additions & 0 deletions src/asc-font.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* asc-font.h (stub)
*
* This header is intentionally provided as a no-op.
*
* In AppStream < 1.0.6, the public header
* appstream-compose.h -> asc-canvas.h
* includes the private header "asc-font.h".
*
* That private header is not installed by default, and nothing in
* the public API actually depends on its declarations.
*
* To allow building against older AppStream releases, we provide
* this empty stub so the include resolves cleanly.
*
* Safe to remove once the minimum required AppStream is >= 1.0.6.
*/
#ifndef __ASC_FONT_H
#define __ASC_FONT_H
#endif
6 changes: 0 additions & 6 deletions src/builder-main.c
Original file line number Diff line number Diff line change
Expand Up @@ -630,12 +630,6 @@ main (int argc,
return 1;
}

if (policy == BUILDER_AS_URL_POLICY_FULL && !appstream_has_version (0, 16, 3))
{
g_printerr ("AppStream version >= 0.16.3 required for 'full' compose URL policy\n");
return 1;
}

builder_context_set_as_url_policy (build_context, policy);
}

Expand Down
168 changes: 113 additions & 55 deletions src/builder-manifest.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@
#include <sys/statfs.h>
#include <glib/gi18n.h>

/* Remove for newer appstream */
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-prototypes"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#endif

#define I_KNOW_THE_APPSTREAM_COMPOSE_API_IS_SUBJECT_TO_CHANGE
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is gonna bite us in the future. :-/

Copy link
Copy Markdown
Contributor Author

@bbhtt bbhtt Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's supposed to get "officially" stable at some point in the future then i can drop all these #664 (comment)

#include <appstream.h>
#include <appstream-compose.h>

#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif

#include "builder-manifest.h"
#include "builder-utils.h"
#include "builder-flatpak-utils.h"
Expand Down Expand Up @@ -2422,30 +2441,74 @@ cmpstringp (const void *p1, const void *p2)
}

static gboolean
appstreamcli_compose (GError **error,
BuilderAsUrlPolicy as_url_policy,
...)
builder_appstreamcli_compose (const gchar *origin,
const gchar *app_id,
const gchar *result_root,
const gchar *data_dir,
const gchar *icon_dir,
const gchar *hint_dir,
const gchar *media_dir,
const gchar *media_baseurl,
BuilderAsUrlPolicy as_url_policy,
GError **error)
{
g_autoptr(GPtrArray) args = NULL;
const gchar *arg;
va_list ap;
g_autoptr(AscCompose) compose = NULL;
g_autoptr(AscDirectoryUnit) dirunit = NULL;
g_autofree gchar *desktop_component = NULL;

args = g_ptr_array_new_with_free_func (g_free);
g_ptr_array_add (args, g_strdup ("appstreamcli"));
g_ptr_array_add (args, g_strdup ("compose"));
g_return_val_if_fail (origin != NULL, FALSE);
g_return_val_if_fail (app_id != NULL, FALSE);
g_return_val_if_fail (result_root != NULL, FALSE);
g_return_val_if_fail (data_dir != NULL, FALSE);
g_return_val_if_fail (icon_dir != NULL, FALSE);
g_return_val_if_fail (hint_dir != NULL, FALSE);

compose = asc_compose_new ();

asc_compose_set_format (compose, AS_FORMAT_KIND_XML);
asc_compose_set_origin (compose, origin);
asc_compose_set_prefix (compose, "/");

asc_compose_add_allowed_cid (compose, app_id);
desktop_component = g_strdup_printf ("%s.desktop", app_id);
asc_compose_add_allowed_cid (compose, desktop_component);

dirunit = asc_directory_unit_new (result_root);
asc_compose_add_unit (compose, ASC_UNIT (dirunit));

asc_compose_set_data_result_dir (compose, data_dir);
asc_compose_set_icons_result_dir (compose, icon_dir);
asc_compose_set_media_baseurl (compose, media_baseurl);
asc_compose_set_media_result_dir (compose, media_dir);
asc_compose_set_hints_result_dir (compose, hint_dir);

#if AS_CHECK_VERSION(0, 16, 3)
if (as_url_policy == BUILDER_AS_URL_POLICY_FULL)
asc_compose_add_flags (compose, ASC_COMPOSE_FLAG_NO_PARTIAL_URLS);
#else
if (as_url_policy == BUILDER_AS_URL_POLICY_FULL)
g_ptr_array_add (args, g_strdup ("--no-partial-urls"));
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
"AppStream >= 0.16.3 required for 'full' compose URL policy "
"(Found: %s)", as_version_string ());
return FALSE;
}
#endif

va_start (ap, as_url_policy);
while ((arg = va_arg (ap, const gchar *)))
g_ptr_array_add (args, g_strdup (arg));
g_ptr_array_add (args, NULL);
va_end (ap);
asc_compose_remove_flags (compose, ASC_COMPOSE_FLAG_ALLOW_SCREENCASTS);
asc_compose_add_flags (compose, ASC_COMPOSE_FLAG_PROPAGATE_CUSTOM);

if (!flatpak_spawnv (NULL, NULL, 0, error, (const char * const *)args->pdata, NULL))
g_autoptr(GPtrArray) results = asc_compose_run (compose, NULL, error);
if (results == NULL)
{
g_prefix_error (error, "ERROR: appstreamcli compose failed: ");
g_prefix_error (error, "AppStream compose failed: ");
return FALSE;
}

if (asc_compose_has_errors (compose))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
Comment thread
bbhtt marked this conversation as resolved.
"AppStream compose completed with errors");
return FALSE;
}

Expand Down Expand Up @@ -3071,59 +3134,54 @@ builder_manifest_cleanup (BuilderManifest *self,

if (self->appstream_compose && appdata_file != NULL)
{
g_autofree char *origin = g_strdup_printf ("--origin=%s",
builder_manifest_get_id (self));
g_autofree char *components_arg = g_strdup_printf ("--components=%s,%s.desktop",
self->id, self->id);
const char *app_root_path = flatpak_file_get_path_cached (app_root);
g_autofree char *result_root_arg = g_strdup_printf ("--result-root=%s", app_root_path);
g_autoptr(GFile) xml_dir = flatpak_build_file (app_root, "share/app-info/xmls", NULL);

g_autoptr(GFile) data_out = flatpak_build_file (app_root, "share/app-info/xmls", NULL);
g_autoptr(GFile) icon_out = flatpak_build_file (app_root, "share/app-info/icons/flatpak", NULL);
g_autoptr(GFile) media_dir = flatpak_build_file (app_root, "share/app-info/media", NULL);
g_autofree char *data_dir = g_strdup_printf ("--data-dir=%s",
flatpak_file_get_path_cached (xml_dir));
g_autofree char *icon_dir = g_strdup_printf ("--icons-dir=%s",
flatpak_file_get_path_cached (icon_out));
g_autoptr(GFile) media_out = flatpak_build_file (app_root, "share/app-info/media", NULL);
g_autoptr(GFile) hint_out = flatpak_build_file (app_root, "appstream", NULL);

const char *data_dir = flatpak_file_get_path_cached (data_out);
const char *icon_dir = flatpak_file_get_path_cached (icon_out);
const char *media_dir = flatpak_file_get_path_cached (media_out);
const char *hint_dir = flatpak_file_get_path_cached (hint_out);
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);

if (opt_mirror_screenshots_url && !opt_export_only)
{
g_autofree char *url = g_build_filename (opt_mirror_screenshots_url, NULL);
g_autofree char *arg_base_url = g_strdup_printf ("--media-baseurl=%s", url);
g_autofree char *arg_media_dir = g_strdup_printf ("--media-dir=%s",
flatpak_file_get_path_cached (media_dir));

g_print ("Running appstreamcli compose\n");
g_print ("Saving screenshots in %s\n", flatpak_file_get_path_cached (media_dir));
if (!appstreamcli_compose (error,
as_url_policy,
"--prefix=/",
origin,
arg_base_url,
arg_media_dir,
result_root_arg,
data_dir,
icon_dir,
components_arg,
app_root_path,
NULL))
return FALSE;
g_print ("Saving screenshots in %s\n", media_dir);
if (!builder_appstreamcli_compose (builder_manifest_get_id (self),
self->id,
app_root_path,
data_dir,
icon_dir,
hint_dir,
media_dir,
url,
as_url_policy,
error))
return FALSE;
}
else
{
g_print ("Running appstreamcli compose\n");
if (!appstreamcli_compose (error,
as_url_policy,
"--prefix=/",
origin,
result_root_arg,
data_dir,
icon_dir,
components_arg,
app_root_path,
NULL))
if (!builder_appstreamcli_compose (builder_manifest_get_id (self),
self->id,
app_root_path,
data_dir,
icon_dir,
hint_dir,
NULL,
NULL,
as_url_policy,
error))
return FALSE;
}
}
Expand Down
52 changes: 0 additions & 52 deletions src/builder-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -1887,55 +1887,3 @@ flatpak_version_check (int major,

return FALSE;
}

gboolean
appstream_has_version (int major,
int minor,
int micro)
{
static int as_major = 0;
static int as_minor = 0;
static int as_micro = 0;

if (as_major == 0 &&
as_minor == 0 &&
as_micro == 0)
{
const char * argv[] = { "appstreamcli", "--version", NULL };
g_autoptr(GSubprocessLauncher) launcher = NULL;
g_autoptr(GSubprocess) subp = NULL;
g_autofree char *out = NULL;
g_auto(GStrv) lines = NULL;

launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
g_subprocess_launcher_setenv (launcher, "LANGUAGE", "C", TRUE);
subp = g_subprocess_launcher_spawnv (launcher, argv, NULL);
g_subprocess_communicate_utf8 (subp, NULL, NULL, &out, NULL, NULL);

lines = g_strsplit (out, "\n", -1);

for (size_t i = 0; lines[i] != NULL; i++)
{
/* Only prefer library version over cli version in case of mismatch */
if (g_str_has_prefix (lines[i], "AppStream library version:"))
{
if (sscanf (lines[i], "AppStream library version: %d.%d.%d", &as_major, &as_minor, &as_micro) == 3)
break;
}
else if (g_str_has_prefix (lines[i], "AppStream version:"))
{
if (sscanf (lines[i], "AppStream version: %d.%d.%d", &as_major, &as_minor, &as_micro) == 3)
break;
}
}

if (as_major == 0 && as_minor == 0 && as_micro == 0)
g_warning ("Failed to find appstream version");
else
g_debug ("Found AppStream version %d.%d.%d", as_major, as_minor, as_micro);
}

return (as_major > major) ||
(as_major == major && as_minor > minor) ||
(as_major == major && as_minor == minor && as_micro >= micro);
}
3 changes: 3 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,15 @@ flatpak_builder_deps = [
dependency('libglnx', default_options: ['tests=false']),
dependency('libxml-2.0', version: '>= 2.4'),
dependency('ostree-1', version: '>= 2017.14'),
dependency('appstream', version: '>=0.15.0'),
dependency('appstream-compose', version: '>=0.15.0'),
yaml_dep,
]

flatpak_builder = executable(
'flatpak-builder',
flatpak_builder_sources,
include_directories: include_directories('.'),
dependencies: flatpak_builder_deps,
install: true,
)
Expand Down