gtk2/tools/gtk-builder-tool-validate.c
Matthias Clasen bcabe77799 buildertool: Warn about deprecations
Add a new option --deprecations to the validate
command that will warn about use of deprecated types.

The list of current deprecations is unfortunately
hardcoded in the source, so this list will have to
be kept up-to-date.

Fixes: #5256
2022-10-19 02:51:29 -04:00

287 lines
7.1 KiB
C

/* Copyright 2015 Red Hat, Inc.
*
* GTK+ 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.
*
* GLib 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 GTK+; see the file COPYING. If not,
* see <http://www.gnu.org/licenses/>.
*
* Author: Matthias Clasen
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <glib/gi18n.h>
#include <glib/gprintf.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include "gtkbuilderprivate.h"
#include "gtk-builder-tool.h"
static GType
make_fake_type (const char *type_name,
const char *parent_name)
{
GType parent_type;
GTypeQuery query;
parent_type = g_type_from_name (parent_name);
if (parent_type == G_TYPE_INVALID)
{
g_printerr ("Failed to lookup template parent type %s\n", parent_name);
exit (1);
}
g_type_query (parent_type, &query);
return g_type_register_static_simple (parent_type,
type_name,
query.class_size,
NULL,
query.instance_size,
NULL,
0);
}
static void
do_validate_template (const char *filename,
const char *type_name,
const char *parent_name)
{
GType template_type;
GObject *object;
GtkBuilder *builder;
GError *error = NULL;
int ret;
/* Only make a fake type if it doesn't exist yet.
* This lets us e.g. validate the GtkFileChooserWidget template.
*/
template_type = g_type_from_name (type_name);
if (template_type == G_TYPE_INVALID)
template_type = make_fake_type (type_name, parent_name);
object = g_object_new (template_type, NULL);
if (!object)
{
g_printerr ("Failed to create an instance of the template type %s\n", type_name);
exit (1);
}
builder = gtk_builder_new ();
ret = gtk_builder_extend_with_template (builder, object , template_type, " ", 1, &error);
if (ret)
ret = gtk_builder_add_from_file (builder, filename, &error);
g_object_unref (builder);
if (ret == 0)
{
g_printerr ("%s\n", error->message);
exit (1);
}
}
static gboolean
parse_template_error (const char *message,
char **class_name,
char **parent_name)
{
char *p;
p = strstr (message, "(class '");
if (p)
{
*class_name = g_strdup (p + strlen ("(class '"));
p = strstr (*class_name, "'");
if (p)
*p = '\0';
}
p = strstr (message, ", parent '");
if (p)
{
*parent_name = g_strdup (p + strlen (", parent '"));
p = strstr (*parent_name, "'");
if (p)
*p = '\0';
}
return *class_name && *parent_name;
}
static gboolean
is_deprecated (GObject *object)
{
const char *names[] = {
"GtkAppChooser",
"GtkAppChooserButton",
"GtkAppChooserDialog",
"GtkAppChooserWidget",
"GtkCellAreaBox",
"GtkCellAreaBoxContext",
"GtkCellArea",
"GtkCellEditable",
"GtkCellLayout",
"GtkCellRendererAccel",
"GtkCellRenderer",
"GtkCellRendererCombo",
"GtkCellRendererPixbuf",
"GtkCellRendererProgress",
"GtkCellRendererSpin",
"GtkCellRendererSpinner",
"GtkCellRendererText",
"GtkCellRendererToggle",
"GtkCellView",
"GtkComboBox",
"GtkComboBoxText",
"GtkEntryCompletion",
"GtkIconView",
"GtkListStore",
"GtkStyleContext",
"GtkTreeModel",
"GtkTreeModelFilter",
"GtkTreeModelSort",
"GtkTreePopover",
"GtkTreeSelection",
"GtkTreeSortable",
"GtkTreeStore",
"GtkTreeView",
"GtkTreeViewColumn",
NULL
};
return g_strv_contains (names, G_OBJECT_TYPE_NAME (object));
}
static const char *
object_get_id (GObject *object)
{
const char *name;
if (GTK_IS_BUILDABLE (object))
name = gtk_buildable_get_buildable_id (GTK_BUILDABLE (object));
else
name = g_object_get_data (object, "gtk-builder-id");
if (g_str_has_prefix (name, "___"))
return NULL;
return name;
}
static gboolean
check_deprecations (GtkBuilder *builder,
GError **error)
{
GSList *objects;
GString *s;
s = g_string_new ("");
objects = gtk_builder_get_objects (builder);
for (GSList *l = objects; l; l = l->next)
{
GObject *obj = l->data;
if (is_deprecated (obj))
{
if (s->len == 0)
g_string_append (s, "Deprecated types:\n");
g_string_append_printf (s, "%s", G_OBJECT_TYPE_NAME (obj));
if (object_get_id (obj))
g_string_append_printf (s, " (named '%s')", object_get_id (obj));
g_string_append (s, "\n");
}
}
g_slist_free (objects);
if (s->len > 0)
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, s->str);
g_string_free (s, TRUE);
return *error == NULL;
}
static gboolean
validate_file (const char *filename,
gboolean deprecations)
{
GtkBuilder *builder;
GError *error = NULL;
int ret;
char *class_name = NULL;
char *parent_name = NULL;
builder = gtk_builder_new ();
ret = gtk_builder_add_from_file (builder, filename, &error);
if (ret && deprecations)
ret = check_deprecations (builder, &error);
g_object_unref (builder);
if (ret == 0)
{
if (g_error_matches (error, GTK_BUILDER_ERROR, GTK_BUILDER_ERROR_UNHANDLED_TAG) &&
parse_template_error (error->message, &class_name, &parent_name))
{
do_validate_template (filename, class_name, parent_name);
}
else
{
g_printerr ("%s\n", error->message);
return FALSE;
}
}
return TRUE;
}
void
do_validate (int *argc, const char ***argv)
{
GError *error = NULL;
char **filenames = NULL;
gboolean deprecations = FALSE;
GOptionContext *context;
const GOptionEntry entries[] = {
{ "deprecations", 0, 0, G_OPTION_ARG_NONE, &deprecations, NULL, NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE") },
{ NULL, }
};
int i;
g_set_prgname ("gtk4-builder-tool validate");
context = g_option_context_new (NULL);
g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
g_option_context_add_main_entries (context, entries, NULL);
g_option_context_set_summary (context, _("Validate the file."));
if (!g_option_context_parse (context, argc, (char ***)argv, &error))
{
g_printerr ("%s\n", error->message);
g_error_free (error);
exit (1);
}
g_option_context_free (context);
for (i = 0; filenames[i]; i++)
{
if (!validate_file (filenames[i], deprecations))
exit (1);
}
g_strfreev (filenames);
}