c10e-html: strip more stuff
[gtk-doc.git] / gtkdoc / scangobj.py
blob4ad27179515aaefa998593d6c7a6bf4f9d134f52
1 # -*- python -*-
3 # gtk-doc - GTK DocBook documentation generator.
4 # Copyright (C) 1998 Damon Chaplin
5 # 2007-2016 Stefan Sauer
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 """
23 The scangobj tool gets information about object hierarchies and signals by
24 compiling and running a small C program. CFLAGS and LDFLAGS must be set
25 appropriately before running this script.
26 """
28 import logging
29 import os
30 import string
31 import subprocess
32 import shlex
34 from . import common, config
37 COMMON_INCLUDES = """
38 #include <string.h>
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <errno.h>
42 #include <glib-object.h>
43 """
45 QUERY_CHILD_PROPS_PROTOTYPE = "extern GParamSpec** %s (gpointer class, guint *n_properties);"
47 QUERY_CHILD_PROPS_CODE = """
48 if (!child_prop) {
49 properties = %s (class, &n_properties);
50 if (properties) {
51 child_prop = TRUE;
52 continue;
55 """
57 MAIN_CODE = """
59 #ifdef GTK_IS_WIDGET_CLASS
60 #include <gtk/gtk.h>
61 #endif
62 static GType object_types[$ntypes];
64 static GType *
65 get_object_types (void)
67 gpointer g_object_class;
68 gint i = 0;
70 ${get_types}
71 object_types[i] = G_TYPE_INVALID;
73 /* reference the GObjectClass to initialize the param spec pool
74 * potentially needed by interfaces. See http://bugs.gnome.org/571820 */
75 g_object_class = g_type_class_ref (G_TYPE_OBJECT);
77 /* Need to make sure all the types are loaded in and initialize
78 * their signals and properties.
80 for (i=0; object_types[i]; i++) {
81 if (G_TYPE_IS_CLASSED (object_types[i]))
82 g_type_class_ref (object_types[i]);
83 if (G_TYPE_IS_INTERFACE (object_types[i]))
84 g_type_default_interface_ref (object_types[i]);
87 g_type_class_unref (g_object_class);
89 return object_types;
93 * This uses GObject type functions to output signal prototypes and the object
94 * hierarchy.
97 /* The output files */
98 const gchar *signals_filename = "$new_signals_filename";
99 const gchar *hierarchy_filename = "$new_hierarchy_filename";
100 const gchar *interfaces_filename = "$new_interfaces_filename";
101 const gchar *prerequisites_filename = "$new_prerequisites_filename";
102 const gchar *args_filename = "$new_args_filename";
104 static void output_signals (void);
105 static void output_object_signals (FILE *fp,
106 GType object_type);
107 static void output_object_signal (FILE *fp,
108 const gchar *object_class_name,
109 guint signal_id);
110 static const gchar * get_type_name (GType type,
111 gboolean * is_pointer);
112 static void output_object_hierarchy (void);
113 static void output_hierarchy (FILE *fp,
114 GType type,
115 guint level);
117 static void output_object_interfaces (void);
118 static void output_interfaces (FILE *fp,
119 GType type);
121 static void output_interface_prerequisites (void);
122 static void output_prerequisites (FILE *fp,
123 GType type);
125 static void output_args (void);
126 static void output_object_args (FILE *fp, GType object_type);
129 main (${main_func_params})
131 ${type_init_func};
133 get_object_types ();
135 output_signals ();
136 output_object_hierarchy ();
137 output_object_interfaces ();
138 output_interface_prerequisites ();
139 output_args ();
141 return 0;
144 static void
145 output_signals (void)
147 FILE *fp;
148 gint i;
150 fp = fopen (signals_filename, "w");
151 if (fp == NULL) {
152 g_warning ("Couldn't open output file: %s : %s", signals_filename, g_strerror(errno));
153 return;
156 for (i = 0; object_types[i]; i++)
157 output_object_signals (fp, object_types[i]);
159 fclose (fp);
162 static gint
163 compare_signals (const void *a, const void *b)
165 const guint *signal_a = a;
166 const guint *signal_b = b;
168 return strcmp (g_signal_name (*signal_a), g_signal_name (*signal_b));
171 /* This outputs all the signals of one object. */
172 static void
173 output_object_signals (FILE *fp, GType object_type)
175 const gchar *object_class_name;
176 guint *signals, n_signals;
177 guint sig;
179 if (G_TYPE_IS_INSTANTIATABLE (object_type) ||
180 G_TYPE_IS_INTERFACE (object_type)) {
182 object_class_name = g_type_name (object_type);
184 signals = g_signal_list_ids (object_type, &n_signals);
185 qsort (signals, n_signals, sizeof (guint), compare_signals);
187 for (sig = 0; sig < n_signals; sig++) {
188 output_object_signal (fp, object_class_name, signals[sig]);
190 g_free (signals);
194 /* This outputs one signal. */
195 static void
196 output_object_signal (FILE *fp,
197 const gchar *object_name,
198 guint signal_id)
200 GSignalQuery query_info;
201 const gchar *type_name, *ret_type, *object_arg, *arg_name;
202 gchar *pos, *object_arg_lower;
203 gboolean is_pointer;
204 gchar buffer[1024];
205 guint i, param;
206 gint param_num, widget_num, event_num, callback_num;
207 gint *arg_num;
208 gchar signal_name[128];
209 gchar flags[16];
211 /* g_print ("Object: %s Signal: %u\\n", object_name, signal_id);*/
213 param_num = 1;
214 widget_num = event_num = callback_num = 0;
216 g_signal_query (signal_id, &query_info);
218 /* Output the signal object type and the argument name. We assume the
219 * type is a pointer - I think that is OK. We remove "Gtk" or "Gnome" and
220 * convert to lower case for the argument name. */
221 pos = buffer;
222 sprintf (pos, "%s ", object_name);
223 pos += strlen (pos);
225 /* Try to come up with a sensible variable name for the first arg
226 * It chops off 2 know prefixes :/ and makes the name lowercase
227 * It should replace lowercase -> uppercase with '_'
228 * GFileMonitor -> file_monitor
229 * GIOExtensionPoint -> extension_point
230 * GtkTreeView -> tree_view
231 * if 2nd char is upper case too
232 * search for first lower case and go back one char
233 * else
234 * search for next upper case
236 if (!strncmp (object_name, "Gtk", 3))
237 object_arg = object_name + 3;
238 else if (!strncmp (object_name, "Gnome", 5))
239 object_arg = object_name + 5;
240 else
241 object_arg = object_name;
243 object_arg_lower = g_ascii_strdown (object_arg, -1);
244 sprintf (pos, "*%s\\n", object_arg_lower);
245 pos += strlen (pos);
246 if (!strncmp (object_arg_lower, "widget", 6))
247 widget_num = 2;
248 g_free(object_arg_lower);
250 /* Convert signal name to use underscores rather than dashes '-'. */
251 strncpy (signal_name, query_info.signal_name, 127);
252 signal_name[127] = '\\0';
253 for (i = 0; signal_name[i]; i++) {
254 if (signal_name[i] == '-')
255 signal_name[i] = '_';
258 /* Output the signal parameters. */
259 for (param = 0; param < query_info.n_params; param++) {
260 type_name = get_type_name (query_info.param_types[param] & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer);
262 /* Most arguments to the callback are called "arg1", "arg2", etc.
263 GtkWidgets are called "widget", "widget2", ...
264 GtkCallbacks are called "callback", "callback2", ... */
265 if (!strcmp (type_name, "GtkWidget")) {
266 arg_name = "widget";
267 arg_num = &widget_num;
269 else if (!strcmp (type_name, "GtkCallback")
270 || !strcmp (type_name, "GtkCCallback")) {
271 arg_name = "callback";
272 arg_num = &callback_num;
274 else {
275 arg_name = "arg";
276 arg_num = &param_num;
278 sprintf (pos, "%s ", type_name);
279 pos += strlen (pos);
281 if (!arg_num || *arg_num == 0)
282 sprintf (pos, "%s%s\\n", is_pointer ? "*" : " ", arg_name);
283 else
284 sprintf (pos, "%s%s%i\\n", is_pointer ? "*" : " ", arg_name,
285 *arg_num);
286 pos += strlen (pos);
288 if (arg_num) {
289 if (*arg_num == 0)
290 *arg_num = 2;
291 else
292 *arg_num += 1;
296 pos = flags;
297 /* We use one-character flags for simplicity. */
298 if (query_info.signal_flags & G_SIGNAL_RUN_FIRST)
299 *pos++ = 'f';
300 if (query_info.signal_flags & G_SIGNAL_RUN_LAST)
301 *pos++ = 'l';
302 if (query_info.signal_flags & G_SIGNAL_RUN_CLEANUP)
303 *pos++ = 'c';
304 if (query_info.signal_flags & G_SIGNAL_NO_RECURSE)
305 *pos++ = 'r';
306 if (query_info.signal_flags & G_SIGNAL_DETAILED)
307 *pos++ = 'd';
308 if (query_info.signal_flags & G_SIGNAL_ACTION)
309 *pos++ = 'a';
310 if (query_info.signal_flags & G_SIGNAL_NO_HOOKS)
311 *pos++ = 'h';
312 *pos = 0;
314 /* Output the return type and function name. */
315 ret_type = get_type_name (query_info.return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE, &is_pointer);
317 fprintf (fp,
318 "<SIGNAL>\\n<NAME>%s::%s</NAME>\\n<RETURNS>%s%s</RETURNS>\\n<FLAGS>%s</FLAGS>\\n%s</SIGNAL>\\n\\n",
319 object_name, query_info.signal_name, ret_type, is_pointer ? "*" : "", flags, buffer);
323 /* Returns the type name to use for a signal argument or return value, given
324 the GtkType from the signal info. It also sets is_pointer to TRUE if the
325 argument needs a '*' since it is a pointer. */
326 static const gchar *
327 get_type_name (GType type, gboolean * is_pointer)
329 const gchar *type_name;
331 *is_pointer = FALSE;
332 type_name = g_type_name (type);
334 switch (type) {
335 case G_TYPE_NONE:
336 case G_TYPE_CHAR:
337 case G_TYPE_UCHAR:
338 case G_TYPE_BOOLEAN:
339 case G_TYPE_INT:
340 case G_TYPE_UINT:
341 case G_TYPE_LONG:
342 case G_TYPE_ULONG:
343 case G_TYPE_FLOAT:
344 case G_TYPE_DOUBLE:
345 case G_TYPE_POINTER:
346 /* These all have normal C type names so they are OK. */
347 return type_name;
349 case G_TYPE_STRING:
350 /* A GtkString is really a gchar*. */
351 *is_pointer = TRUE;
352 return "gchar";
354 case G_TYPE_ENUM:
355 case G_TYPE_FLAGS:
356 /* We use a gint for both of these. Hopefully a subtype with a decent
357 name will be registered and used instead, as GTK+ does itself. */
358 return "gint";
360 case G_TYPE_BOXED:
361 /* The boxed type shouldn't be used itself, only subtypes. Though we
362 return 'gpointer' just in case. */
363 return "gpointer";
365 case G_TYPE_PARAM:
366 /* A GParam is really a GParamSpec*. */
367 *is_pointer = TRUE;
368 return "GParamSpec";
370 #if GLIB_CHECK_VERSION (2, 25, 9)
371 case G_TYPE_VARIANT:
372 *is_pointer = TRUE;
373 return "GVariant";
374 #endif
376 default:
377 break;
380 /* For all GObject subclasses we can use the class name with a "*",
381 e.g. 'GtkWidget *'. */
382 if (g_type_is_a (type, G_TYPE_OBJECT))
383 *is_pointer = TRUE;
385 /* Also catch non GObject root types */
386 if (G_TYPE_IS_CLASSED (type))
387 *is_pointer = TRUE;
389 /* All boxed subtypes will be pointers as well. */
390 /* Exception: GStrv */
391 if (g_type_is_a (type, G_TYPE_BOXED) &&
392 !g_type_is_a (type, G_TYPE_STRV))
393 *is_pointer = TRUE;
395 /* All pointer subtypes will be pointers as well. */
396 if (g_type_is_a (type, G_TYPE_POINTER))
397 *is_pointer = TRUE;
399 /* But enums are not */
400 if (g_type_is_a (type, G_TYPE_ENUM) ||
401 g_type_is_a (type, G_TYPE_FLAGS))
402 *is_pointer = FALSE;
404 return type_name;
408 /* This outputs the hierarchy of all objects which have been initialized,
409 i.e. by calling their XXX_get_type() initialization function. */
410 static void
411 output_object_hierarchy (void)
413 FILE *fp;
414 gint i,j;
415 GType root, type;
416 GType root_types[$ntypes] = { G_TYPE_INVALID, };
418 fp = fopen (hierarchy_filename, "w");
419 if (fp == NULL) {
420 g_warning ("Couldn't open output file: %s : %s", hierarchy_filename, g_strerror(errno));
421 return;
423 output_hierarchy (fp, G_TYPE_OBJECT, 0);
424 output_hierarchy (fp, G_TYPE_INTERFACE, 0);
426 for (i=0; object_types[i]; i++) {
427 root = object_types[i];
428 while ((type = g_type_parent (root))) {
429 root = type;
431 if ((root != G_TYPE_OBJECT) && (root != G_TYPE_INTERFACE)) {
432 for (j=0; root_types[j]; j++) {
433 if (root == root_types[j]) {
434 root = G_TYPE_INVALID; break;
437 if(root) {
438 root_types[j] = root;
439 output_hierarchy (fp, root, 0);
444 fclose (fp);
447 /* This is called recursively to output the hierarchy of a object. */
448 static void
449 output_hierarchy (FILE *fp,
450 GType type,
451 guint level)
453 guint i;
454 GType *children;
455 guint n_children;
457 if (!type)
458 return;
460 for (i = 0; i < level; i++)
461 fprintf (fp, " ");
462 fprintf (fp, "%s\\n", g_type_name (type));
464 children = g_type_children (type, &n_children);
466 for (i=0; i < n_children; i++)
467 output_hierarchy (fp, children[i], level + 1);
469 g_free (children);
472 static void output_object_interfaces (void)
474 guint i;
475 FILE *fp;
477 fp = fopen (interfaces_filename, "w");
478 if (fp == NULL) {
479 g_warning ("Couldn't open output file: %s : %s", interfaces_filename, g_strerror(errno));
480 return;
482 output_interfaces (fp, G_TYPE_OBJECT);
484 for (i = 0; object_types[i]; i++) {
485 if (!g_type_parent (object_types[i]) &&
486 (object_types[i] != G_TYPE_OBJECT) &&
487 G_TYPE_IS_INSTANTIATABLE (object_types[i])) {
488 output_interfaces (fp, object_types[i]);
491 fclose (fp);
494 static void
495 output_interfaces (FILE *fp,
496 GType type)
498 guint i;
499 GType *children, *interfaces;
500 guint n_children, n_interfaces;
502 if (!type)
503 return;
505 interfaces = g_type_interfaces (type, &n_interfaces);
507 if (n_interfaces > 0) {
508 fprintf (fp, "%s", g_type_name (type));
509 for (i=0; i < n_interfaces; i++)
510 fprintf (fp, " %s", g_type_name (interfaces[i]));
511 fprintf (fp, "\\n");
513 g_free (interfaces);
515 children = g_type_children (type, &n_children);
517 for (i=0; i < n_children; i++)
518 output_interfaces (fp, children[i]);
520 g_free (children);
523 static void output_interface_prerequisites (void)
525 FILE *fp;
527 fp = fopen (prerequisites_filename, "w");
528 if (fp == NULL) {
529 g_warning ("Couldn't open output file: %s : %s", prerequisites_filename, g_strerror(errno));
530 return;
532 output_prerequisites (fp, G_TYPE_INTERFACE);
533 fclose (fp);
536 static void
537 output_prerequisites (FILE *fp,
538 GType type)
540 #if GLIB_CHECK_VERSION(2,1,0)
541 guint i;
542 GType *children, *prerequisites;
543 guint n_children, n_prerequisites;
545 if (!type)
546 return;
548 prerequisites = g_type_interface_prerequisites (type, &n_prerequisites);
550 if (n_prerequisites > 0) {
551 fprintf (fp, "%s", g_type_name (type));
552 for (i=0; i < n_prerequisites; i++)
553 fprintf (fp, " %s", g_type_name (prerequisites[i]));
554 fprintf (fp, "\\n");
556 g_free (prerequisites);
558 children = g_type_children (type, &n_children);
560 for (i=0; i < n_children; i++)
561 output_prerequisites (fp, children[i]);
563 g_free (children);
564 #endif
567 static void
568 output_args (void)
570 FILE *fp;
571 gint i;
573 fp = fopen (args_filename, "w");
574 if (fp == NULL) {
575 g_warning ("Couldn't open output file: %s : %s", args_filename, g_strerror(errno));
576 return;
579 for (i = 0; object_types[i]; i++) {
580 output_object_args (fp, object_types[i]);
583 fclose (fp);
586 static gint
587 compare_param_specs (const void *a, const void *b)
589 GParamSpec *spec_a = *(GParamSpec **)a;
590 GParamSpec *spec_b = *(GParamSpec **)b;
592 return strcmp (g_param_spec_get_name (spec_a), g_param_spec_get_name (spec_b));
595 /* Its common to have unsigned properties restricted
596 * to the signed range. Therefore we make this look
597 * a bit nicer by spelling out the max constants.
600 /* Don't use "==" with floats, it might trigger a gcc warning. */
601 #define GTKDOC_COMPARE_FLOAT(x, y) (x <= y && x >= y)
603 static gchar*
604 describe_double_constant (gdouble value)
606 gchar *desc;
608 if (GTKDOC_COMPARE_FLOAT (value, G_MAXDOUBLE))
609 desc = g_strdup ("G_MAXDOUBLE");
610 else if (GTKDOC_COMPARE_FLOAT (value, G_MINDOUBLE))
611 desc = g_strdup ("G_MINDOUBLE");
612 else if (GTKDOC_COMPARE_FLOAT (value, -G_MAXDOUBLE))
613 desc = g_strdup ("-G_MAXDOUBLE");
614 else if (GTKDOC_COMPARE_FLOAT (value, G_MAXFLOAT))
615 desc = g_strdup ("G_MAXFLOAT");
616 else if (GTKDOC_COMPARE_FLOAT (value, G_MINFLOAT))
617 desc = g_strdup ("G_MINFLOAT");
618 else if (GTKDOC_COMPARE_FLOAT (value, -G_MAXFLOAT))
619 desc = g_strdup ("-G_MAXFLOAT");
620 else{
621 /* make sure floats are output with a decimal dot irrespective of
622 * current locale. Use formatd since we want human-readable numbers
623 * and do not need the exact same bit representation when deserialising */
624 desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE);
625 g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g", value);
628 return desc;
631 static gchar*
632 describe_signed_constant (gsize size, gint64 value)
634 gchar *desc = NULL;
636 switch (size) {
637 case 2:
638 if (sizeof (int) == 2) {
639 if (value == G_MAXINT)
640 desc = g_strdup ("G_MAXINT");
641 else if (value == G_MININT)
642 desc = g_strdup ("G_MININT");
644 break;
645 case 4:
646 if (sizeof (int) == 4) {
647 if (value == G_MAXINT)
648 desc = g_strdup ("G_MAXINT");
649 else if (value == G_MININT)
650 desc = g_strdup ("G_MININT");
652 if (value == G_MAXLONG)
653 desc = g_strdup ("G_MAXLONG");
654 else if (value == G_MINLONG)
655 desc = g_strdup ("G_MINLONG");
656 break;
657 case 8:
658 if (value == G_MAXINT64)
659 desc = g_strdup ("G_MAXINT64");
660 else if (value == G_MININT64)
661 desc = g_strdup ("G_MININT64");
662 break;
663 default:
664 break;
666 if (!desc)
667 desc = g_strdup_printf ("%" G_GINT64_FORMAT, value);
669 return desc;
672 static gchar*
673 describe_unsigned_constant (gsize size, guint64 value)
675 gchar *desc = NULL;
677 switch (size) {
678 case 2:
679 if (sizeof (int) == 2) {
680 if (value == (guint64)G_MAXINT)
681 desc = g_strdup ("G_MAXINT");
682 else if (value == G_MAXUINT)
683 desc = g_strdup ("G_MAXUINT");
685 break;
686 case 4:
687 if (sizeof (int) == 4) {
688 if (value == (guint64)G_MAXINT)
689 desc = g_strdup ("G_MAXINT");
690 else if (value == G_MAXUINT)
691 desc = g_strdup ("G_MAXUINT");
693 if (value == (guint64)G_MAXLONG)
694 desc = g_strdup ("G_MAXLONG");
695 else if (value == G_MAXULONG)
696 desc = g_strdup ("G_MAXULONG");
697 break;
698 case 8:
699 if (value == G_MAXINT64)
700 desc = g_strdup ("G_MAXINT64");
701 else if (value == G_MAXUINT64)
702 desc = g_strdup ("G_MAXUINT64");
703 break;
704 default:
705 break;
707 if (!desc)
708 desc = g_strdup_printf ("%" G_GUINT64_FORMAT, value);
710 return desc;
713 static gchar*
714 describe_type (GParamSpec *spec)
716 gchar *desc;
717 gchar *lower;
718 gchar *upper;
720 if (G_IS_PARAM_SPEC_CHAR (spec)) {
721 GParamSpecChar *pspec = G_PARAM_SPEC_CHAR (spec);
723 lower = describe_signed_constant (sizeof(gchar), pspec->minimum);
724 upper = describe_signed_constant (sizeof(gchar), pspec->maximum);
725 if (pspec->minimum == G_MININT8 && pspec->maximum == G_MAXINT8)
726 desc = g_strdup ("");
727 else if (pspec->minimum == G_MININT8)
728 desc = g_strdup_printf ("<= %s", upper);
729 else if (pspec->maximum == G_MAXINT8)
730 desc = g_strdup_printf (">= %s", lower);
731 else
732 desc = g_strdup_printf ("[%s,%s]", lower, upper);
733 g_free (lower);
734 g_free (upper);
736 else if (G_IS_PARAM_SPEC_UCHAR (spec)) {
737 GParamSpecUChar *pspec = G_PARAM_SPEC_UCHAR (spec);
739 lower = describe_unsigned_constant (sizeof(guchar), pspec->minimum);
740 upper = describe_unsigned_constant (sizeof(guchar), pspec->maximum);
741 if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT8)
742 desc = g_strdup ("");
743 else if (pspec->minimum == 0)
744 desc = g_strdup_printf ("<= %s", upper);
745 else if (pspec->maximum == G_MAXUINT8)
746 desc = g_strdup_printf (">= %s", lower);
747 else
748 desc = g_strdup_printf ("[%s,%s]", lower, upper);
749 g_free (lower);
750 g_free (upper);
752 else if (G_IS_PARAM_SPEC_INT (spec)) {
753 GParamSpecInt *pspec = G_PARAM_SPEC_INT (spec);
755 lower = describe_signed_constant (sizeof(gint), pspec->minimum);
756 upper = describe_signed_constant (sizeof(gint), pspec->maximum);
757 if (pspec->minimum == G_MININT && pspec->maximum == G_MAXINT)
758 desc = g_strdup ("");
759 else if (pspec->minimum == G_MININT)
760 desc = g_strdup_printf ("<= %s", upper);
761 else if (pspec->maximum == G_MAXINT)
762 desc = g_strdup_printf (">= %s", lower);
763 else
764 desc = g_strdup_printf ("[%s,%s]", lower, upper);
765 g_free (lower);
766 g_free (upper);
768 else if (G_IS_PARAM_SPEC_UINT (spec)) {
769 GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (spec);
771 lower = describe_unsigned_constant (sizeof(guint), pspec->minimum);
772 upper = describe_unsigned_constant (sizeof(guint), pspec->maximum);
773 if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT)
774 desc = g_strdup ("");
775 else if (pspec->minimum == 0)
776 desc = g_strdup_printf ("<= %s", upper);
777 else if (pspec->maximum == G_MAXUINT)
778 desc = g_strdup_printf (">= %s", lower);
779 else
780 desc = g_strdup_printf ("[%s,%s]", lower, upper);
781 g_free (lower);
782 g_free (upper);
784 else if (G_IS_PARAM_SPEC_LONG (spec)) {
785 GParamSpecLong *pspec = G_PARAM_SPEC_LONG (spec);
787 lower = describe_signed_constant (sizeof(glong), pspec->minimum);
788 upper = describe_signed_constant (sizeof(glong), pspec->maximum);
789 if (pspec->minimum == G_MINLONG && pspec->maximum == G_MAXLONG)
790 desc = g_strdup ("");
791 else if (pspec->minimum == G_MINLONG)
792 desc = g_strdup_printf ("<= %s", upper);
793 else if (pspec->maximum == G_MAXLONG)
794 desc = g_strdup_printf (">= %s", lower);
795 else
796 desc = g_strdup_printf ("[%s,%s]", lower, upper);
797 g_free (lower);
798 g_free (upper);
800 else if (G_IS_PARAM_SPEC_ULONG (spec)) {
801 GParamSpecULong *pspec = G_PARAM_SPEC_ULONG (spec);
803 lower = describe_unsigned_constant (sizeof(gulong), pspec->minimum);
804 upper = describe_unsigned_constant (sizeof(gulong), pspec->maximum);
805 if (pspec->minimum == 0 && pspec->maximum == G_MAXULONG)
806 desc = g_strdup ("");
807 else if (pspec->minimum == 0)
808 desc = g_strdup_printf ("<= %s", upper);
809 else if (pspec->maximum == G_MAXULONG)
810 desc = g_strdup_printf (">= %s", lower);
811 else
812 desc = g_strdup_printf ("[%s,%s]", lower, upper);
813 g_free (lower);
814 g_free (upper);
816 else if (G_IS_PARAM_SPEC_INT64 (spec)) {
817 GParamSpecInt64 *pspec = G_PARAM_SPEC_INT64 (spec);
819 lower = describe_signed_constant (sizeof(gint64), pspec->minimum);
820 upper = describe_signed_constant (sizeof(gint64), pspec->maximum);
821 if (pspec->minimum == G_MININT64 && pspec->maximum == G_MAXINT64)
822 desc = g_strdup ("");
823 else if (pspec->minimum == G_MININT64)
824 desc = g_strdup_printf ("<= %s", upper);
825 else if (pspec->maximum == G_MAXINT64)
826 desc = g_strdup_printf (">= %s", lower);
827 else
828 desc = g_strdup_printf ("[%s,%s]", lower, upper);
829 g_free (lower);
830 g_free (upper);
832 else if (G_IS_PARAM_SPEC_UINT64 (spec)) {
833 GParamSpecUInt64 *pspec = G_PARAM_SPEC_UINT64 (spec);
835 lower = describe_unsigned_constant (sizeof(guint64), pspec->minimum);
836 upper = describe_unsigned_constant (sizeof(guint64), pspec->maximum);
837 if (pspec->minimum == 0 && pspec->maximum == G_MAXUINT64)
838 desc = g_strdup ("");
839 else if (pspec->minimum == 0)
840 desc = g_strdup_printf ("<= %s", upper);
841 else if (pspec->maximum == G_MAXUINT64)
842 desc = g_strdup_printf (">= %s", lower);
843 else
844 desc = g_strdup_printf ("[%s,%s]", lower, upper);
845 g_free (lower);
846 g_free (upper);
848 else if (G_IS_PARAM_SPEC_FLOAT (spec)) {
849 GParamSpecFloat *pspec = G_PARAM_SPEC_FLOAT (spec);
851 lower = describe_double_constant (pspec->minimum);
852 upper = describe_double_constant (pspec->maximum);
853 if (GTKDOC_COMPARE_FLOAT (pspec->minimum, -G_MAXFLOAT)) {
854 if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXFLOAT))
855 desc = g_strdup ("");
856 else
857 desc = g_strdup_printf ("<= %s", upper);
859 else if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXFLOAT))
860 desc = g_strdup_printf (">= %s", lower);
861 else
862 desc = g_strdup_printf ("[%s,%s]", lower, upper);
863 g_free (lower);
864 g_free (upper);
866 else if (G_IS_PARAM_SPEC_DOUBLE (spec)) {
867 GParamSpecDouble *pspec = G_PARAM_SPEC_DOUBLE (spec);
869 lower = describe_double_constant (pspec->minimum);
870 upper = describe_double_constant (pspec->maximum);
871 if (GTKDOC_COMPARE_FLOAT (pspec->minimum, -G_MAXDOUBLE)) {
872 if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXDOUBLE))
873 desc = g_strdup ("");
874 else
875 desc = g_strdup_printf ("<= %s", upper);
877 else if (GTKDOC_COMPARE_FLOAT (pspec->maximum, G_MAXDOUBLE))
878 desc = g_strdup_printf (">= %s", lower);
879 else
880 desc = g_strdup_printf ("[%s,%s]", lower, upper);
881 g_free (lower);
882 g_free (upper);
884 #if GLIB_CHECK_VERSION (2, 12, 0)
885 else if (G_IS_PARAM_SPEC_GTYPE (spec)) {
886 GParamSpecGType *pspec = G_PARAM_SPEC_GTYPE (spec);
887 gboolean is_pointer;
889 desc = g_strdup (get_type_name (pspec->is_a_type, &is_pointer));
891 #endif
892 #if GLIB_CHECK_VERSION (2, 25, 9)
893 else if (G_IS_PARAM_SPEC_VARIANT (spec)) {
894 GParamSpecVariant *pspec = G_PARAM_SPEC_VARIANT (spec);
895 gchar *variant_type;
897 variant_type = g_variant_type_dup_string (pspec->type);
898 desc = g_strdup_printf ("GVariant<%s>", variant_type);
899 g_free (variant_type);
901 #endif
902 else {
903 desc = g_strdup ("");
906 return desc;
909 static gchar*
910 describe_default (GParamSpec *spec)
912 gchar *desc;
914 if (G_IS_PARAM_SPEC_CHAR (spec)) {
915 GParamSpecChar *pspec = G_PARAM_SPEC_CHAR (spec);
917 desc = g_strdup_printf ("%d", pspec->default_value);
919 else if (G_IS_PARAM_SPEC_UCHAR (spec)) {
920 GParamSpecUChar *pspec = G_PARAM_SPEC_UCHAR (spec);
922 desc = g_strdup_printf ("%u", pspec->default_value);
924 else if (G_IS_PARAM_SPEC_BOOLEAN (spec)) {
925 GParamSpecBoolean *pspec = G_PARAM_SPEC_BOOLEAN (spec);
927 desc = g_strdup_printf ("%s", pspec->default_value ? "TRUE" : "FALSE");
929 else if (G_IS_PARAM_SPEC_INT (spec)) {
930 GParamSpecInt *pspec = G_PARAM_SPEC_INT (spec);
932 desc = g_strdup_printf ("%d", pspec->default_value);
934 else if (G_IS_PARAM_SPEC_UINT (spec)) {
935 GParamSpecUInt *pspec = G_PARAM_SPEC_UINT (spec);
937 desc = g_strdup_printf ("%u", pspec->default_value);
939 else if (G_IS_PARAM_SPEC_LONG (spec)) {
940 GParamSpecLong *pspec = G_PARAM_SPEC_LONG (spec);
942 desc = g_strdup_printf ("%ld", pspec->default_value);
944 else if (G_IS_PARAM_SPEC_LONG (spec)) {
945 GParamSpecULong *pspec = G_PARAM_SPEC_ULONG (spec);
947 desc = g_strdup_printf ("%lu", pspec->default_value);
949 else if (G_IS_PARAM_SPEC_INT64 (spec)) {
950 GParamSpecInt64 *pspec = G_PARAM_SPEC_INT64 (spec);
952 desc = g_strdup_printf ("%" G_GINT64_FORMAT, pspec->default_value);
954 else if (G_IS_PARAM_SPEC_UINT64 (spec))
956 GParamSpecUInt64 *pspec = G_PARAM_SPEC_UINT64 (spec);
958 desc = g_strdup_printf ("%" G_GUINT64_FORMAT, pspec->default_value);
960 else if (G_IS_PARAM_SPEC_UNICHAR (spec)) {
961 GParamSpecUnichar *pspec = G_PARAM_SPEC_UNICHAR (spec);
963 if (g_unichar_isprint (pspec->default_value))
964 desc = g_strdup_printf ("'%c'", pspec->default_value);
965 else
966 desc = g_strdup_printf ("%u", pspec->default_value);
968 else if (G_IS_PARAM_SPEC_ENUM (spec)) {
969 GParamSpecEnum *pspec = G_PARAM_SPEC_ENUM (spec);
971 GEnumValue *value = g_enum_get_value (pspec->enum_class, pspec->default_value);
972 if (value)
973 desc = g_strdup_printf ("%s", value->value_name);
974 else
975 desc = g_strdup_printf ("%d", pspec->default_value);
977 else if (G_IS_PARAM_SPEC_FLAGS (spec)) {
978 GParamSpecFlags *pspec = G_PARAM_SPEC_FLAGS (spec);
979 guint default_value;
980 GString *acc;
982 default_value = pspec->default_value;
983 acc = g_string_new ("");
985 while (default_value) {
986 GFlagsValue *value = g_flags_get_first_value (pspec->flags_class, default_value);
988 if (!value)
989 break;
991 if (acc->len > 0)
992 g_string_append (acc, " | ");
993 g_string_append (acc, value->value_name);
995 default_value &= ~value->value;
998 if (default_value == 0)
999 desc = g_string_free (acc, FALSE);
1000 else {
1001 desc = g_strdup_printf ("%d", pspec->default_value);
1002 g_string_free (acc, TRUE);
1005 else if (G_IS_PARAM_SPEC_FLOAT (spec)) {
1006 GParamSpecFloat *pspec = G_PARAM_SPEC_FLOAT (spec);
1008 /* make sure floats are output with a decimal dot irrespective of
1009 * current locale. Use formatd since we want human-readable numbers
1010 * and do not need the exact same bit representation when deserialising */
1011 desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE);
1012 g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g",
1013 pspec->default_value);
1015 else if (G_IS_PARAM_SPEC_DOUBLE (spec)) {
1016 GParamSpecDouble *pspec = G_PARAM_SPEC_DOUBLE (spec);
1018 /* make sure floats are output with a decimal dot irrespective of
1019 * current locale. Use formatd since we want human-readable numbers
1020 * and do not need the exact same bit representation when deserialising */
1021 desc = g_malloc0 (G_ASCII_DTOSTR_BUF_SIZE);
1022 g_ascii_formatd (desc, G_ASCII_DTOSTR_BUF_SIZE, "%g",
1023 pspec->default_value);
1025 else if (G_IS_PARAM_SPEC_STRING (spec)) {
1026 GParamSpecString *pspec = G_PARAM_SPEC_STRING (spec);
1028 if (pspec->default_value) {
1029 gchar *esc = g_strescape (pspec->default_value, NULL);
1030 desc = g_strdup_printf ("\\"%s\\"", esc);
1031 g_free (esc);
1033 else
1034 desc = g_strdup_printf ("NULL");
1036 #if GLIB_CHECK_VERSION (2, 25, 9)
1037 else if (G_IS_PARAM_SPEC_VARIANT (spec)) {
1038 GParamSpecVariant *pspec = G_PARAM_SPEC_VARIANT (spec);
1040 if (pspec->default_value)
1041 desc = g_variant_print (pspec->default_value, TRUE);
1042 else
1043 desc = g_strdup ("NULL");
1045 #endif
1046 else {
1047 desc = g_strdup ("");
1050 return desc;
1054 static void
1055 output_object_args (FILE *fp, GType object_type)
1057 gpointer class;
1058 const gchar *object_class_name;
1059 guint arg;
1060 gchar flags[16], *pos;
1061 GParamSpec **properties;
1062 guint n_properties;
1063 gboolean child_prop;
1064 gboolean style_prop;
1065 gboolean is_pointer;
1066 const gchar *type_name;
1067 gchar *type_desc;
1068 gchar *default_value;
1070 if (G_TYPE_IS_OBJECT (object_type)) {
1071 class = g_type_class_peek (object_type);
1072 if (!class)
1073 return;
1075 properties = g_object_class_list_properties (class, &n_properties);
1077 #if GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 3)
1078 else if (G_TYPE_IS_INTERFACE (object_type)) {
1079 class = g_type_default_interface_ref (object_type);
1081 if (!class)
1082 return;
1084 properties = g_object_interface_list_properties (class, &n_properties);
1086 #endif
1087 else
1088 return;
1090 object_class_name = g_type_name (object_type);
1092 child_prop = FALSE;
1093 style_prop = FALSE;
1095 while (TRUE) {
1096 qsort (properties, n_properties, sizeof (GParamSpec *), compare_param_specs);
1097 for (arg = 0; arg < n_properties; arg++) {
1098 GParamSpec *spec = properties[arg];
1099 const gchar *nick, *blurb, *dot;
1101 if (spec->owner_type != object_type)
1102 continue;
1104 pos = flags;
1105 /* We use one-character flags for simplicity. */
1106 if (child_prop && !style_prop)
1107 *pos++ = 'c';
1108 if (style_prop)
1109 *pos++ = 's';
1110 if (spec->flags & G_PARAM_READABLE)
1111 *pos++ = 'r';
1112 if (spec->flags & G_PARAM_WRITABLE)
1113 *pos++ = 'w';
1114 if (spec->flags & G_PARAM_CONSTRUCT)
1115 *pos++ = 'x';
1116 if (spec->flags & G_PARAM_CONSTRUCT_ONLY)
1117 *pos++ = 'X';
1118 *pos = 0;
1120 nick = g_param_spec_get_nick (spec);
1121 blurb = g_param_spec_get_blurb (spec);
1123 dot = "";
1124 if (blurb) {
1125 int str_len = strlen (blurb);
1126 if (str_len > 0 && blurb[str_len - 1] != '.')
1127 dot = ".";
1130 type_desc = describe_type (spec);
1131 default_value = describe_default (spec);
1132 type_name = get_type_name (spec->value_type, &is_pointer);
1133 fprintf (fp, "<ARG>\\n"
1134 "<NAME>%s::%s</NAME>\\n"
1135 "<TYPE>%s%s</TYPE>\\n"
1136 "<RANGE>%s</RANGE>\\n"
1137 "<FLAGS>%s</FLAGS>\\n"
1138 "<NICK>%s</NICK>\\n"
1139 "<BLURB>%s%s</BLURB>\\n"
1140 "<DEFAULT>%s</DEFAULT>\\n"
1141 "</ARG>\\n\\n",
1142 object_class_name, g_param_spec_get_name (spec), type_name,
1143 is_pointer ? "*" : "", type_desc, flags, nick ? nick : "(null)",
1144 blurb ? blurb : "(null)", dot, default_value);
1145 g_free (type_desc);
1146 g_free (default_value);
1149 g_free (properties);
1151 #ifdef GTK_IS_CONTAINER_CLASS
1152 if (!child_prop && GTK_IS_CONTAINER_CLASS (class)) {
1153 properties = gtk_container_class_list_child_properties (class, &n_properties);
1154 child_prop = TRUE;
1155 continue;
1157 #endif
1159 #ifdef GTK_IS_CELL_AREA_CLASS
1160 if (!child_prop && GTK_IS_CELL_AREA_CLASS (class)) {
1161 properties = gtk_cell_area_class_list_cell_properties (class, &n_properties);
1162 child_prop = TRUE;
1163 continue;
1165 #endif
1167 #ifdef GTK_IS_WIDGET_CLASS
1168 #if GTK_CHECK_VERSION(2,1,0) && !GTK_CHECK_VERSION(3,89,2)
1169 if (!style_prop && GTK_IS_WIDGET_CLASS (class)) {
1170 properties = gtk_widget_class_list_style_properties (GTK_WIDGET_CLASS (class), &n_properties);
1171 style_prop = TRUE;
1172 continue;
1174 #endif
1175 #endif
1179 MAIN_CODE_END = """
1180 break;
1186 def execute_command(options, description, command):
1187 if options.verbose:
1188 call = subprocess.check_call
1189 else:
1190 call = subprocess.check_output
1192 try:
1193 call(command)
1194 except subprocess.CalledProcessError as e:
1195 logging.warning('%s scanner failed: %d, command: %s', description,
1196 e.returncode, ' '.join(command))
1197 return e.returncode
1198 except OSError as e:
1199 logging.warning('%s scanner failed: %s, command: %s', description,
1200 str(e), ' '.join(command))
1201 return 1
1202 return 0
1205 def run(options):
1206 logging.info('options: %s', str(options.__dict__))
1208 c_file = options.module + '-scan.c'
1209 output = common.open_text(c_file, 'w')
1211 base_filename = os.path.join(options.output_dir, options.module)
1212 old_signals_filename = base_filename + '.signals'
1213 new_signals_filename = base_filename + '.signals.new'
1214 old_hierarchy_filename = base_filename + '.hierarchy'
1215 new_hierarchy_filename = base_filename + '.hierarchy.new'
1216 old_interfaces_filename = base_filename + '.interfaces'
1217 new_interfaces_filename = base_filename + '.interfaces.new'
1218 old_prerequisites_filename = base_filename + '.prerequisites'
1219 new_prerequisites_filename = base_filename + '.prerequisites.new'
1220 old_args_filename = base_filename + '.args'
1221 new_args_filename = base_filename + '.args.new'
1223 # generate a C program to scan the types
1225 includes = ""
1226 forward_decls = ""
1227 get_types = ""
1228 ntypes = 1
1230 for line in common.open_text(options.types):
1231 if line.startswith('#include'):
1232 includes += line
1233 elif line.startswith('%') or line.strip() == '':
1234 continue
1235 else:
1236 line = line.strip()
1237 get_types += ' object_types[i++] = ' + line + ' ();\n'
1238 forward_decls += 'extern GType ' + line + ' (void);\n'
1239 ntypes += 1
1241 output.write(COMMON_INCLUDES)
1243 if includes:
1244 output.write(includes)
1245 else:
1246 output.write(forward_decls)
1248 if options.query_child_properties:
1249 output.write(QUERY_CHILD_PROPS_PROTOTYPE % options.query_child_properties)
1251 # substitute local vars in the template
1252 type_init_func = options.type_init_func
1253 main_func_params = "int argc, char *argv[]"
1254 if "argc" in type_init_func and "argv" not in type_init_func:
1255 main_func_params = "int argc, G_GNUC_UNUSED char *argv[]"
1256 elif "argc" not in type_init_func and "argv" in type_init_func:
1257 main_func_params = "G_GNUC_UNUSED int argc, char *argv[]"
1258 elif "argc" not in type_init_func and "argv" not in type_init_func:
1259 main_func_params = "void"
1261 output.write(string.Template(MAIN_CODE).substitute(locals()))
1263 if options.query_child_properties:
1264 output.write(QUERY_CHILD_PROPS_CODE % options.query_child_properties)
1266 output.write(MAIN_CODE_END)
1268 output.close()
1270 # Compile and run our file
1271 if 'libtool' in options.cc:
1272 o_file = options.module + '-scan.lo'
1273 else:
1274 o_file = options.module + '-scan.o'
1276 x_file = options.module + '-scan' + config.exeext
1278 logging.debug('Intermediate scanner files: %s, %s, %s', c_file, o_file, x_file)
1280 res = execute_command(options, 'Compiling',
1281 shlex.split(options.cc) + shlex.split(options.cflags) +
1282 ["-c", "-o", o_file, c_file])
1283 if res:
1284 return res
1286 res = execute_command(options, 'Linking',
1287 shlex.split(options.ld) + [o_file] +
1288 shlex.split(options.ldflags) + ['-o', x_file])
1289 if res:
1290 return res
1292 res = execute_command(options, 'Running',
1293 shlex.split(options.run) + ['./' + x_file])
1294 if res:
1295 return res
1297 logging.debug('Scan complete')
1298 if 'GTK_DOC_KEEP_INTERMEDIATE' not in os.environ:
1299 os.unlink(c_file)
1300 os.unlink(o_file)
1301 os.unlink(x_file)
1302 if 'libtool' in options.cc:
1303 o_file = options.module + '-scan.o'
1304 if os.path.exists(o_file):
1305 os.unlink(o_file)
1306 else:
1307 logging.debug('Keeping generated sources for analysis: %s, %s, %s',
1308 c_file, o_file, x_file)
1310 common.UpdateFileIfChanged(old_signals_filename, new_signals_filename, False)
1311 common.UpdateFileIfChanged(old_hierarchy_filename, new_hierarchy_filename, False)
1312 common.UpdateFileIfChanged(old_interfaces_filename, new_interfaces_filename, False)
1313 common.UpdateFileIfChanged(old_prerequisites_filename, new_prerequisites_filename, False)
1314 common.UpdateFileIfChanged(old_args_filename, new_args_filename, False)
1316 return 0