gapplication: add GApplication:version property

This property is supposed to be used by authors of applications that use GAppliaction to output the version by --version flag or otherwise if a version is needed.

Closes #3198

Signed-off-by: Maxim Moskalets <Maxim.Moskalets@kaspersky.com>
This commit is contained in:
Maxim Moskalets 2024-02-09 10:04:14 +00:00 committed by Philip Withnall
parent 59d454a20d
commit cbcb35a77a
4 changed files with 145 additions and 1 deletions

View File

@ -227,6 +227,7 @@ struct _GApplicationPrivate
{ {
GApplicationFlags flags; GApplicationFlags flags;
gchar *id; gchar *id;
gchar *version;
gchar *resource_path; gchar *resource_path;
GActionGroup *actions; GActionGroup *actions;
@ -264,6 +265,7 @@ enum
{ {
PROP_NONE, PROP_NONE,
PROP_APPLICATION_ID, PROP_APPLICATION_ID,
PROP_VERSION,
PROP_FLAGS, PROP_FLAGS,
PROP_RESOURCE_BASE_PATH, PROP_RESOURCE_BASE_PATH,
PROP_IS_REGISTERED, PROP_IS_REGISTERED,
@ -478,11 +480,13 @@ g_application_pack_option_entries (GApplication *application,
static GVariantDict * static GVariantDict *
g_application_parse_command_line (GApplication *application, g_application_parse_command_line (GApplication *application,
gchar ***arguments, gchar ***arguments,
gboolean *print_version,
GError **error) GError **error)
{ {
gboolean become_service = FALSE; gboolean become_service = FALSE;
gchar *app_id = NULL; gchar *app_id = NULL;
gboolean replace = FALSE; gboolean replace = FALSE;
gboolean version = FALSE;
GVariantDict *dict = NULL; GVariantDict *dict = NULL;
GOptionContext *context; GOptionContext *context;
GOptionGroup *gapplication_group; GOptionGroup *gapplication_group;
@ -564,6 +568,17 @@ g_application_parse_command_line (GApplication *application,
g_option_group_add_entries (gapplication_group, entries); g_option_group_add_entries (gapplication_group, entries);
} }
if (application->priv->version)
{
GOptionEntry entries[] = {
{ "version", '\0', 0, G_OPTION_ARG_NONE, &version,
N_("Print the application version"), NULL },
G_OPTION_ENTRY_NULL
};
g_option_group_add_entries (gapplication_group, entries);
}
/* Allow replacing if the application allows it */ /* Allow replacing if the application allows it */
if (application->priv->flags & G_APPLICATION_ALLOW_REPLACEMENT) if (application->priv->flags & G_APPLICATION_ALLOW_REPLACEMENT)
{ {
@ -580,6 +595,8 @@ g_application_parse_command_line (GApplication *application,
if (!g_option_context_parse_strv (context, arguments, error)) if (!g_option_context_parse_strv (context, arguments, error))
goto out; goto out;
*print_version = version;
/* Check for --gapplication-service */ /* Check for --gapplication-service */
if (become_service) if (become_service)
application->priv->flags |= G_APPLICATION_IS_SERVICE; application->priv->flags |= G_APPLICATION_IS_SERVICE;
@ -1101,8 +1118,9 @@ g_application_real_local_command_line (GApplication *application,
GError *error = NULL; GError *error = NULL;
GVariantDict *options; GVariantDict *options;
gint n_args; gint n_args;
gboolean print_version = FALSE;
options = g_application_parse_command_line (application, arguments, &error); options = g_application_parse_command_line (application, arguments, &print_version, &error);
if (!options) if (!options)
{ {
g_printerr ("%s\n", error->message); g_printerr ("%s\n", error->message);
@ -1111,6 +1129,21 @@ g_application_real_local_command_line (GApplication *application,
return TRUE; return TRUE;
} }
/* Exit quickly with --version? */
if (print_version)
{
const char *prgname = g_get_prgname ();
g_assert (application->priv->version != NULL);
if (prgname != NULL)
g_print ("%s %s\n", prgname, application->priv->version);
else
g_print ("%s\n", application->priv->version);
*exit_status = EXIT_SUCCESS;
return TRUE;
}
g_signal_emit (application, g_application_signals[SIGNAL_HANDLE_LOCAL_OPTIONS], 0, options, exit_status); g_signal_emit (application, g_application_signals[SIGNAL_HANDLE_LOCAL_OPTIONS], 0, options, exit_status);
if (*exit_status >= 0) if (*exit_status >= 0)
@ -1236,6 +1269,10 @@ g_application_set_property (GObject *object,
g_value_get_string (value)); g_value_get_string (value));
break; break;
case PROP_VERSION:
g_application_set_version (application, g_value_get_string (value));
break;
case PROP_FLAGS: case PROP_FLAGS:
g_application_set_flags (application, g_value_get_flags (value)); g_application_set_flags (application, g_value_get_flags (value));
break; break;
@ -1306,6 +1343,11 @@ g_application_get_property (GObject *object,
g_application_get_application_id (application)); g_application_get_application_id (application));
break; break;
case PROP_VERSION:
g_value_set_string (value,
g_application_get_version (application));
break;
case PROP_FLAGS: case PROP_FLAGS:
g_value_set_flags (value, g_value_set_flags (value,
g_application_get_flags (application)); g_application_get_flags (application));
@ -1401,6 +1443,7 @@ g_application_finalize (GObject *object)
g_free (application->priv->parameter_string); g_free (application->priv->parameter_string);
g_free (application->priv->summary); g_free (application->priv->summary);
g_free (application->priv->description); g_free (application->priv->description);
g_free (application->priv->version);
g_slist_free_full (application->priv->option_strings, g_free); g_slist_free_full (application->priv->option_strings, g_free);
@ -1496,6 +1539,18 @@ g_application_class_init (GApplicationClass *class)
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS)); G_PARAM_STATIC_STRINGS));
/**
* GApplication:version:
*
* The human-readable version number of the application.
*
* Since: 2.80
*/
g_object_class_install_property (object_class, PROP_VERSION,
g_param_spec_string ("version", NULL, NULL,
NULL, G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY));
/** /**
* GApplication:flags: * GApplication:flags:
* *
@ -1887,6 +1942,50 @@ g_application_set_application_id (GApplication *application,
} }
} }
/**
* g_application_get_version:
* @application: a #GApplication
*
* Gets the version of @application.
*
* Returns: (nullable): the version of @application
*
* Since: 2.80
**/
const gchar *
g_application_get_version (GApplication *application)
{
g_return_val_if_fail (G_IS_APPLICATION (application), NULL);
return application->priv->version;
}
/**
* g_application_set_version
* @application: a #GApplication
* @version: the version of @application
*
* Sets the version number of @application. This will be used to implement
* a `--version` command line argument
*
* The application version can only be modified if @application has not yet
* been registered.
*
* Since: 2.80
**/
void
g_application_set_version (GApplication *application,
const gchar *version)
{
g_return_if_fail (G_IS_APPLICATION (application));
g_return_if_fail (version != NULL);
g_return_if_fail (!application->priv->is_registered);
if (g_set_str (&application->priv->version, version))
g_object_notify (G_OBJECT (application), "version");
}
/** /**
* g_application_get_flags: * g_application_get_flags:
* @application: a #GApplication * @application: a #GApplication

View File

@ -139,6 +139,12 @@ GIO_AVAILABLE_IN_ALL
void g_application_set_application_id (GApplication *application, void g_application_set_application_id (GApplication *application,
const gchar *application_id); const gchar *application_id);
GIO_AVAILABLE_IN_2_80
const gchar * g_application_get_version (GApplication *application);
GIO_AVAILABLE_IN_2_80
void g_application_set_version (GApplication *application,
const gchar *version);
GIO_AVAILABLE_IN_2_34 GIO_AVAILABLE_IN_2_34
GDBusConnection * g_application_get_dbus_connection (GApplication *application); GDBusConnection * g_application_get_dbus_connection (GApplication *application);
GIO_AVAILABLE_IN_2_34 GIO_AVAILABLE_IN_2_34

View File

@ -34,6 +34,7 @@ main (int argc, char **argv)
G_APPLICATION_HANDLES_COMMAND_LINE); G_APPLICATION_HANDLES_COMMAND_LINE);
g_signal_connect (app, "command-line", G_CALLBACK (command_line), NULL); g_signal_connect (app, "command-line", G_CALLBACK (command_line), NULL);
g_application_set_inactivity_timeout (app, 10000); g_application_set_inactivity_timeout (app, 10000);
g_application_set_version (app, "2.3");
status = g_application_run (app, argc, argv); status = g_application_run (app, argc, argv);

View File

@ -369,6 +369,7 @@ properties (void)
GDBusConnection *c; GDBusConnection *c;
GObject *app; GObject *app;
gchar *id; gchar *id;
gchar *version;
GApplicationFlags flags; GApplicationFlags flags;
gboolean registered; gboolean registered;
guint timeout; guint timeout;
@ -381,20 +382,25 @@ properties (void)
app = g_object_new (G_TYPE_APPLICATION, app = g_object_new (G_TYPE_APPLICATION,
"application-id", "org.gtk.TestApplication", "application-id", "org.gtk.TestApplication",
"version", "1.0",
NULL); NULL);
g_object_get (app, g_object_get (app,
"application-id", &id, "application-id", &id,
"version", &version,
"flags", &flags, "flags", &flags,
"is-registered", &registered, "is-registered", &registered,
"inactivity-timeout", &timeout, "inactivity-timeout", &timeout,
NULL); NULL);
g_assert_cmpstr (id, ==, "org.gtk.TestApplication"); g_assert_cmpstr (id, ==, "org.gtk.TestApplication");
g_assert_cmpstr (version, ==, "1.0");
g_assert_cmpint (flags, ==, G_APPLICATION_DEFAULT_FLAGS); g_assert_cmpint (flags, ==, G_APPLICATION_DEFAULT_FLAGS);
g_assert (!registered); g_assert (!registered);
g_assert_cmpint (timeout, ==, 0); g_assert_cmpint (timeout, ==, 0);
g_clear_pointer (&version, g_free);
ret = g_application_register (G_APPLICATION (app), NULL, &error); ret = g_application_register (G_APPLICATION (app), NULL, &error);
g_assert (ret); g_assert (ret);
g_assert_no_error (error); g_assert_no_error (error);
@ -1085,6 +1091,37 @@ test_api (void)
g_object_unref (app); g_object_unref (app);
} }
static void
test_version (void)
{
GApplication *app;
gchar *version = NULL;
gchar *version_orig = NULL;
app = g_application_new ("org.gtk.TestApplication", 0);
version_orig = "1.2";
g_object_set (G_OBJECT (app), "version", version_orig, NULL);
g_object_get (app, "version", &version, NULL);
g_assert_cmpstr (version, ==, version_orig);
g_free (version);
/* test attempting to set the same version again */
version_orig = "1.2";
g_object_set (G_OBJECT (app), "version", version_orig, NULL);
g_object_get (app, "version", &version, NULL);
g_assert_cmpstr (version, ==, version_orig);
g_free (version);
version_orig = "2.4";
g_object_set (G_OBJECT (app), "version", version_orig, NULL);
g_object_get (app, "version", &version, NULL);
g_assert_cmpstr (version, ==, version_orig);
g_free (version);
g_object_unref (app);
}
/* Check that G_APPLICATION_ALLOW_REPLACEMENT works. To do so, we launch /* Check that G_APPLICATION_ALLOW_REPLACEMENT works. To do so, we launch
* a GApplication in this process that allows replacement, and then * a GApplication in this process that allows replacement, and then
* launch a subprocess with --gapplication-replace. We have to do our * launch a subprocess with --gapplication-replace. We have to do our
@ -1830,6 +1867,7 @@ main (int argc, char **argv)
g_test_add_func ("/gapplication/test-handle-local-options2", test_handle_local_options_failure); g_test_add_func ("/gapplication/test-handle-local-options2", test_handle_local_options_failure);
g_test_add_func ("/gapplication/test-handle-local-options3", test_handle_local_options_passthrough); g_test_add_func ("/gapplication/test-handle-local-options3", test_handle_local_options_passthrough);
g_test_add_func ("/gapplication/api", test_api); g_test_add_func ("/gapplication/api", test_api);
g_test_add_func ("/gapplication/version", test_version);
g_test_add_data_func ("/gapplication/replace", GINT_TO_POINTER (TRUE), test_replace); g_test_add_data_func ("/gapplication/replace", GINT_TO_POINTER (TRUE), test_replace);
g_test_add_data_func ("/gapplication/no-replace", GINT_TO_POINTER (FALSE), test_replace); g_test_add_data_func ("/gapplication/no-replace", GINT_TO_POINTER (FALSE), test_replace);
g_test_add_func ("/gapplication/dbus/activate", test_dbus_activate); g_test_add_func ("/gapplication/dbus/activate", test_dbus_activate);